skip to Main Content

im creating a wrapper to check authentication the server response is as expected but somehow the isAuthenticated state does not change to true.

import React, { useState, useEffect } from "react";
import { Navigate } from "react-router-dom";
import Cookies from "js-cookie";
import axios from "axios";

function PrivateRoute({ children }) {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  async function checkAuth() {
    try {
      axios.defaults.withCredentials = true;
      const sessionId = Cookies.get("sessionId");
      const data = {
        sessionId: sessionId,
      };
      if (sessionId) {
        const response = await axios.get("http://127.0.0.1:5000/session", {
          params: data,
        });
        console.log(response.status);
        if (response.status === 200) {
          console.log("authenticated");
          setIsAuthenticated(true);
        } else {
          console.log("unauthenticated");
          setIsAuthenticated(false);
        }
      }
    } catch (err) {
      console.error(err);
      setIsAuthenticated(false);
    }
  }

  checkAuth();
  console.log(isAuthenticated);
  return isAuthenticated ? children : <Navigate to="/login" />;
}

export default PrivateRoute;


ive tried to log inside my if statement and it works and when i log isAuthenticated after calling the checkAuth function it remains false.

2

Answers


  1. function PrivateRoute({ children }) {
      const [isAuthenticated, setIsAuthenticated] = useState(false);
    
      async function checkAuth() {
        // api call and set isAuthenticated
      }
    
      useEffect(() => {
        checkAuth();
      }, []);
    
      // should expecte this line to run twice
      // false for the first time as the component first mounted (1st render)
      // ture or false for the second time when the api call is completed (2nd render)
      console.log(isAuthenticated);
    
      return isAuthenticated ? children : <Navigate to="/login" />;
    }
    
    export default PrivateRoute;
    
    1. the checkAuth in your code is an async function so console.log(isAuthenticated); is ran before setIsAuthenticated(true);

    2. The set function only updates the state variable for the next render. If you read the state variable after calling the set function, you will still get the old value that was on the screen before your call. doc

    3. Use useEffect instead of calling the function directly. doc

    Login or Signup to reply.
  2. As others have mentioned, React batches state updates in order to optimize performance, meaning setState runs "asynchronously" (though it’s not a Promise-returning function).

    If you’re not using a client-side caching library like useSWR, then per the React documentation, the proper approach is to do data fetching within a useEffect(). Just make sure to have an ignore flag to avoid potential race conditions, as well as add support for a null state for when the request is still loading (a.k.a. before you decide to give the user access or navigate them away). In the end, your component should look something like this:

    import React, { useState, useEffect } from "react";
    import { Navigate } from "react-router-dom";
    import Cookies from "js-cookie";
    import axios from "axios";
    
    function PrivateRoute({ children }) {
        const [isAuthenticated, setIsAuthenticated] = useState(null);
        const sessionId = Cookies.get("sessionId");
    
        useEffect(() => {
            let ignore = false;
            async function checkAuth() {
                try {
                    axios.defaults.withCredentials = true;
                    if (sessionId) {
                        const response = await axios.get("http://127.0.0.1:5000/session", { params: { sessionId } });
                        const isSuccess = response.status === 200;
                        if (!ignore) {
                            console.log(isSuccess ? "authenticated" : "unauthenticated");
                            setIsAuthenticated(isSuccess);
                        }
                    }
                } catch (err) {
                    if (!ignore) {
                        console.error(err);
                        setIsAuthenticated(false);
                    }
                }
            }
    
            checkAuth();
            return () => { ignore = true; };
        }, [sessionId]);
    
        console.log(isAuthenticated);
    
        if (isAuthenticated === null) return "Loading...";
    
        return isAuthenticated ? children : <Navigate to="/login" />;
    }
    
    export default PrivateRoute;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search