custom hook and private route in React immediately navigates to login

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 }} />;
}

Leave a ReplyCancel reply