I want the user to be redirected to the home page after successfully logging in, I store the data of the api request to log in the user in a variable called currentUser, so if currentUser is true it should redirect to home.
this is how i handle the login request :
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(
JSON.parse(localStorage.getItem("users")) || null
);
const login = async (inputs) => {
const res= await axios.post("https://micacarballo-social-media-api.onrender.com/api/v1/auth/login",inputs,{
})
setCurrentUser(res.data)
};
useEffect(() => {
localStorage.setItem("users", JSON.stringify(currentUser));
}, [currentUser,login]);
const updateUser = async () =>{
const user = JSON.parse(localStorage.getItem("users"))
const res= await axios.get("https://micacarballo-social-media-api.onrender.com/api/v1/users/me/",{
headers: {
Authorization: `jwt ${user.token}`
}
})
setCurrentUser(res.data)
}
return (
<AuthContext.Provider value={{ currentUser, login , updateUser}}>
{children}
</AuthContext.Provider>
);
};
My protected routes :
import { AuthContext } from './context/authContext'
function App() {
const { currentUser } = useContext(AuthContext);
const Layout = ()=>{
return(
<div >
<Navbar />
<div style={{ display: "flex" }}>
<LeftBar />
<div style={{ flex: 6 }}>
<Outlet />
</div>
</div>
</div>
)
}
const ProtectedRoute = ({children}) =>{
if(!currentUser){
return <Navigate to="/login" />
}return children
}
const router = createBrowserRouter([
{
path: "/",
element: (
<ProtectedRoute>
<Layout/>
</ProtectedRoute>
),
children :[
{
path:"/",
element: <Home />
},{
path:"/profile/:id",
element:<Profile />
}
]
}, {
path: "/login",
element: <Login />,
},
{
path: "/register",
element: <Register />
},
]);
return (
<div className="App">
<RouterProvider router={router} />
</div>
)
}
export default App
and my login page :
const Login = () => {
const [inputs, setInputs] = useState({
email: "",
password: "",
});
const [err, setErr] = useState(null);
const [isLoading, setIsLoading] = useState(false); // state variable for loading spinner
const navigate = useNavigate()
const handleChange = (e) => {
setInputs((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const {login} = useContext(AuthContext)
const handleLogin = async (e) => {
e.preventDefault();
try {
setIsLoading(true); // set loading to true
await login(inputs);
navigate("/")
} catch (err) {
setErr(err.response.data);
}finally {
setIsLoading(false); // set loading back to false
}
};
return (
<div className='login'>
<div className="card">
<div className="left">
<h1>SocialMica</h1>
<p>
Make connecting with friends easy and fun
</p>
<span>Dont have an account yet?</span>
<Link to="/register">
<button>Register</button>
</Link>
</div>
<div className="right">
<h1>Login</h1>
<form action="">
<input type="email" placeholder='email' name='email' onChange={handleChange}></input>
<input type="password"placeholder='password' name='password' onChange={handleChange}/>
{err && "invalid email or password"}
<button onClick={handleLogin} disabled={isLoading}> {isLoading ? <Loading/> : "Login"}</button>
</form>
</div>
</div>
</div> )
}
export default Login
I dont understand why its not working, after the user is logged in and currentUser stores the data, the login page just refresh and doesnt redirect,I have to manually access to the home page.
I tried changing the navigate to ("/register) in the handleSubmit function in the login page and it works properly but not with navigate(‘/’)
2
Answers
change your path
/home
not only/
just like
it should working
Issue
The issue is that in the
handleLogin
callback thelogin
handler enqueues acurrentUser
state update and in the same render cycle enqueues a navigation action. When the navigation action is immediately processed thecurrentUser
state is yet to be updated and theAuthContextProvider
hasn’t rerendered with the updatedcurrentUser
state for the UI to read. This means thatProtectedRoute
reads the not-yet-updatedcurrentUser
state value and redirects back to"/login"
. The reason redirecting to"/register"
works is because it’s not rendered on a protected route.Solution Suggestions
To get around this you need to update the code to not enqueue the state update and navigation request in the same render cycle.
Method 1: delay the
navigate
call to the end of the Javascript event queue in thehandleLogin
handler.Method 2: delay the return of the
login
call to the end of the Javascript event queueDemo