Advertisements
I have a very straight forward private Route in React:
export default function PrivateRoute({ children }) {
const auth = useAuth();
return auth ? children : <Navigate to="/login" />;
}
and this is my custom hook:
export default function useAuth() {
const [loggedIn, setLoggedIn] = useState(false);
useEffect(() => {
const token = Cookies.get("token");
axios
.get(`${process.env.REACT_APP_API}/auth`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((result) => {
if (result.status === 200) {
setLoggedIn(true);
}
})
.catch((error) => console.error(error));
}, []);
return loggedIn;
}
The hook successfully returns true or false, but at the time when the PrivateRoute
is called, it is false, thus I land on the login page.
How can I wait for the hook before redirecting to login?
I call the PrivateRoute like this:
<Route
path="/admin"
exact
element={
<PrivateRoute>
<Admin />
</PrivateRoute>
}
/>
``
>Solution :
The initial loggedIn
state value matches the unauthenticated state. Use an indeterminant value that doesn’t match either the confirmed authenticated/unauthenticated status. Check for this indeterminant value and conditionally render null or some loading indicator.
export default function useAuth() {
const [loggedIn, setLoggedIn] = useState(); // <-- undefined
useEffect(() => {
const token = Cookies.get("token");
axios
.get(`${process.env.REACT_APP_API}/auth`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((result) => {
setLoggedIn(result.status === 200);
})
.catch((error) => {
console.error(error)
setLoggedIn(false);
});
}, []);
return loggedIn;
}
…
export default function PrivateRoute({ children }) {
const location = useLocation();
const auth = useAuth();
if (auth === undefined) return null; // or loading spinner/etc...
return auth
? children
: <Navigate to="/login" replace state={{ from: location }} />;
}