skip to Main Content

I have a problem with asynchronous states on React

I use context to determine whether the admin is authenticated and has the right to access a page with a state called isAuth

interface UserContext {
    setToken: (token:string) => void;
    getToken: () => string|null;
    logout: () => void;
    setIsAuth: (value:boolean) => void;
    isAuth: boolean;
}

const initialState: UserContext = {
    setToken: () => {}, 
    getToken: () => null,
    logout: () => {},
    setIsAuth: () => {},
    isAuth: false,
}

const UserContext = createContext<UserContext>(initialState)

export function UserContextProvider ({children}:{children:React.ReactNode}){
    const [isAuth, setIsAuth] = useState(false);

    console.log("isAuth in UserContextProvider",isAuth);

    const setToken = (token:string) => {
        window.localStorage.setItem('token', token);
    }

    const getToken = () => {
        return window.localStorage.getItem('token');
    }

    const logout = () => {
        //redirect("/login")
        window.localStorage.removeItem('token');
    }


    return <UserContext.Provider value={{setToken, getToken, logout, setIsAuth, isAuth }}>
        {children}
    </UserContext.Provider>
}

export const useUserContext = () => {
    return useContext(UserContext)
}

main.ts

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <UserContextProvider>
      <App />
    </UserContextProvider>
  </React.StrictMode>,
)

In App, I make an API call to check if the admin is authenticated, and I set isAuth to response.code === 200 (if authenticated, the response is 200). In this case, the admin is authenticated so the response is 200 and isAuth is set to true

function App() {
  const {setIsAuth,getToken} = useUserContext();

  useEffect(() => {
    const token = getToken();

    fetch(import.meta.env.VITE_API_URL+"/isAuth", {
      headers: {Authorization: `Bearer ${token}`}
    })
    .then((res) => res.json())
    .then((response) => {
        console.log("response App",response);
        setIsAuth(response.code === 200);
    })
    
  }, [location.pathname])

In the ProjectTable component (the admin page that displays projects), I need to check if he is authenticated, and if not, redirect to /login

I made a log of isAuth

export function ProjectTable({projects}:{projects:Project[]}){
    const { t } = useTranslation();
    const {isAuth} = useUserContext();
    const navigateTo = useNavigate();

    useEffect(() => {
        
        console.log("isAuth in useEffect ProjectTable",isAuth);

        // if (!isAuth){
        //     navigateTo("/login");
        // }

    }, [])

Here is the console output when I am on the admin page ProjectTable, which displays the projects

logs

isAuth in UserContextProvider is false and isAuth in ProjectTable is false.

Then I make the API call and set isAuth to true.

isAuth in UserContextProvider is set to true, but not in ProjectTable. I don’t see the log indicating that isAuth is true; I only see the old log showing it as false before isAuth in UserContextProvider becomes true.

So, if I uncomment if (!isAuth), I am redirected to /login even though I shouldn’t be because I am authenticated

2

Answers


  1. Chosen as BEST ANSWER

    With isAuth printed in the function component :

    export function ProjectTable({projects}:{projects:Project[]}){
        const {isAuth} = useUserContext();
        const navigateTo = useNavigate();
    
        console.log("isAuth in function ProjectTable",isAuth);
    
        // if (!isAuth){
        //     navigateTo("/login");
        // }
    

    logs-function-componant

    If I uncomment

    if (!isAuth){
         navigateTo("/login");
    }
    

    I am redirected to /login because at the beginning isAuth is false

    With isAuth in the useEffect dependency :

    export function ProjectTable({projects}:{projects:Project[]}){
        const {isAuth} = useUserContext();
        const navigateTo = useNavigate();
        
        useEffect(() => {
            
            console.log("isAuth in useEffect ProjectTable",isAuth);
    
            // if (!isAuth){
            //     navigateTo("/login");
            // }
    
        }, [isAuth])
    

    logs-useeffect

    If I uncomment

    if (!isAuth){
         navigateTo("/login");
    }
    

    I am redirected to /login because at the beginning isAuth is false


  2. You should print isAuth in the function component. Otherwise, the value of isAuth is the stale value when the component mounted.

    export function ProjectTable({projects}:{projects:Project[]}){
      const {isAuth} = useUserContext();
    
      console.log("isAuth in useEffect ProjectTable",isAuth)
    }
    

    Or, add it to the dependency list of useEffect hook

    export function ProjectTable({projects}:{projects:Project[]}){
      const {isAuth} = useUserContext();
    
      useEffect(() => {
        console.log("isAuth in useEffect ProjectTable",isAuth)
      }, [isAuth]);
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search