The app should redirect to /dashboard
when user
is not null
. User is null
in the first render but useEffect()
should pick up and store user data from local storage. Then, the ternary operator should trigger <Redirect to='/dashboard' />
and redirects to dashboard page. But, it's not doing that. It just keeps showing the login page at /login
whenever I refresh even if there is data in localStorage.
What am I doing wrong or how can I solve this?
const App = () => {
const [user, setUser] = useState(null)
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
noteService.setToken(user.token)
}
}, [])
return (
<Router>
<Switch>
<Route exact path='/'>
{user
? <Redirect to='/dashboard' />
: <Redirect to='/login' />
}
</Route>
<Route exact path='/login'>
<LogIn />
</Route>
<Route exact path='/dashboard'>
<Dashboard />
</Route>
</Switch>
</Router>
)
CodePudding user response:
You are not doing it the common way. Try this out :
create PrivateRoute.js
import { Redirect, Route } from "react-router-dom";
const PrivateRoute = ({user, ...rest}) => {
return (
<Route
{...rest}
render={(props) =>
user ? (
<Redirect to='/dashboard' {..props}/>
) : (
<Redirect to="/login" {..props} />
)
}
/>
);
};
export default PrivateRoute;
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Import it and do so :
const App = () => {
const [user, setUser] = useState(null)
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
noteService.setToken(user.token)
}
}, [])
return (
<Router>
<Switch>
<PrivateRoute exact path='/' user= {user}/>
<Route exact path='/login'>
<LogIn />
</Route>
<Route exact path='/dashboard'>
<Dashboard />
</Route>
</Switch>
</Router>
)
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Issue
The issue is that on the initial render the user
state is null
and the redirect to "/login" occurs. When you reload the page, if the path is still "/login" it doesn't exactly match "/" and no redirect occurs, regardless of user
state.
Solution
Initialize state from localStorage instead of waiting a render cycle. This at least allows you to check the user
state on the first render instead of on the second (or later) rerender. Reorder the routes in the Switch
component to specify them from more specific to less specific. The redirect should come last so other paths have a chance to be matched and rendered first.
const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));
const App = () => {
const [user] = useState(initializeState());
useEffect(() => {
noteService.setToken(user.token);
}, [user]);
return (
<Router>
<Switch>
<Route path='/login'>
<LogIn />
</Route>
<Route path='/dashboard'>
<Dashboard />
</Route>
<Redirect to={user ? '/dashboard' : '/login'} />
</Switch>
</Router>
);
};
This doesn't quite resolve the page reload issue where a user is already on a sub-route. For this you should implement a ProtectedRoute
component and pass the user
state as a prop. If there is an user
object it renders a regular Route
component and passes the props on through, otherwise it renders a Redirect
and bounces the user to the login route.
const ProtectedRoute = ({ user, ...props }) => user ? (
<Route {...props} />
) : (
<Redirect to="/login" />
);
const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));
const App = () => {
const [user] = useState(initializeState());
useEffect(() => {
noteService.setToken(user.token);
}, [user]);
return (
<Router>
<Switch>
<Route path='/login'>
<LogIn />
</Route>
<ProtectedRoute path='/dashboard' user={user} >
<Dashboard />
</ProtectedRoute>
<Redirect to={user ? '/dashboard' : '/login'} />
</Switch>
</Router>
);
};