'React Router: How to stop redirecting until API returns a response?

I have a login page but that authenticates the login on router with a call to the backend. The backend returns a response 200 if the user is authenticated, and 400 if its not.

This is my code chunk for the private route

export default function PrivateRoute({ component: Component, ...rest }: { component: any }) {
    // https://stackoverflow.com/questions/65193640/how-to-detect-jwt-token-expire-on-react
    const [userLoggedIn, setUserLoggedIn] = useState(false);

    useEffect(() => {
        // If acess_token cannot be found, set expiration to current time to force login
        const jwtToken = localStorage.getItem('sycamore_access_token')

        axiosInstance.post('/auth/authenticatetoken/', {
            token: jwtToken,
            user: localStorage.getItem("user")
        })
            .then(res => {
                setUserLoggedIn(true)
                console.log(userLoggedIn, "A")
            })
            .catch(error => {
                setUserLoggedIn(false)
                console.log(userLoggedIn, "B")
            })

    }, [])

and returns the component if its authenticated

return (
        <React.Fragment>
            {console.log(userLoggedIn)}
            <Route
                {...rest}
                render={(props) =>
                    (userLoggedIn) ? (
                        <Component {...props} />
                    ) : (
                        <Redirect
                            to={{ pathname: '/Login', state: { from: props.location } }}
                        />
                    )
                }
            />
        </React.Fragment>

The problem here is that the state userLoggedIn does not seem to update immediately, and returns false -> authenticated -> true, by which then it has already redirected me back to the login page again. Any idea why this is happening or how to solve this?



Solution 1:[1]

The problem here is that the state userLoggedIn does not seem to update immediately

That's correct, userLoggedIn will change once the post request returns a response.

You can add a state like loading/inProgress and use it to show a loading component/text until the API returns a response.

const [userLoggedIn, setUserLoggedIn] = useState(false);
const [loading, setLoading] = useState(true);

useEffect(() => {
  const jwtToken = localStorage.getItem('sycamore_access_token');
  
  // initial value of loading is `true` so setLoading(true) is unnecessary

  axiosInstance
    .post('/auth/authenticatetoken/', {
      token: jwtToken,
      user: localStorage.getItem('user'),
    })
    .then((res) => {
      setUserLoggedIn(true);
      console.log(userLoggedIn, 'A');
    })
    .catch((error) => {
      setUserLoggedIn(false);
      console.log(userLoggedIn, 'B');
    })
    .finally(() => {
        // setting loading to false
        setLoading(false);
    });
}, []);

Now you can return early if loading is true. This will prevent the redirect check until loading changes to false.

if(loading) return <p>Loading...</p>

return (<React.Fragment>...);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1