To give access to authenticated users only, I created a component PrivateRoute like described here. Everything works fine, but it gives me a warning, that 'location' is undefined.
import React from 'react';
import PropTypes from 'prop-types';
import { Redirect, Route } from 'react-router-dom';
import auth from '../../hooks/login/auth';
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={(props) => (
auth.getToken() !== null ? (
<Component {...props} />
) : (
<Redirect to={{
pathname: '/log_in',
state: { from: props.location },
}}
/>
)
)}
/>
);
PrivateRoute.propTypes = {
component: PropTypes.any.isRequired, // eslint-disable-line
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
};
export default PrivateRoute;
<Switch>
<Route exact path="/" component={LogIn} />
<Route exact path="/log_in" component={LogIn} />
<PrivateRoute exact path="/dashboard" component={Dashboard} />
</Switch>
If I make a console log in the render method, location is always defined. So wtf?
<Route
{...rest}
render={(props) => {
console.log('LOCATION', props.location);
return auth.getToken() !== null ? (
<Component {...props} />
) : (
<Redirect to={{
pathname: '/log_in',
state: { from: props.location },
}}
/>
);
}}
/>
I have tried to give location a default value, but then the whole page was blank.
PrivateRoute.propTypes = {
component: PropTypes.any.isRequired, // eslint-disable-line
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}),
};
PrivateRoute.defaultProps = {
location: {
pathname: '',
},
};
What is the problem here? And how is it possible that location is undefined when any console log output shows a defined location object?
You are declaring a location PropType for the PrivateRoute component, which will obviously be undefined since you aren't passing a location prop to your PrivateRoute component when rendering, and you are logging the route props of the Route component that the PrivateRoute is rendering.
const PrivateRoute = ({
component: Component, // <-- not logging any of these props
...rest
}) => (
<Route
{...rest}
render={(props) => ( // <-- logging these route props
auth.getToken() !== null ? (
<Component {...props} />
) : (
<Redirect to={{
pathname: '/log_in',
state: { from: props.location },
}}
/>
)
)}
/>
);
location is not even a Route component prop, it's just one of the props passed to all three render methods (component, render, and children function).
If you want/need to specify that the location.pathname prop is available then this should be done on the component that is being rendered by your custom PrivateRoute component as this is where a location prop is passed. In this case, it is the Dashboard component that should have location.pathname defined as a required prop.
If it helps, there is a react-router-prop-types package you can install and use. It exports a location PropType declaration with the pathname prop already required.
export const location = PropTypes.shape({ hash: PropTypes.string.isRequired, key: PropTypes.string, // only in createBrowserHistory and createMemoryHistory pathname: PropTypes.string.isRequired, search: PropTypes.string.isRequired, state: PropTypes.oneOfType([ PropTypes.array, PropTypes.bool, PropTypes.number, PropTypes.object, PropTypes.string, ]), // only in createBrowserHistory and createMemoryHistory });
In Dashboard:
import { location } from 'react-router-prop-types';
Dashboard.propTypes = {
// ... the other prop types
location: location.isRequired,
};