Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

187
Vistas
React Router Dom, redirection happens before the code inside useEffect gets executed

I am attempting to use React's useEffect hook to run a fetch command and store the result in state. I would then like to use that state value to conditionally render a React Route component. I am having trouble setting the value of that variable in state before the component runs the conditional statement and returns the Route component.

Express code:

app.post('/refresh_token_from_db', async (req: Request, res: Response) => {
    res.send(true)
})

ProtectedRoutes:

import { Navigate, Outlet } from 'react-router-dom'
import { useState, useEffect } from 'react';

function ProtectedRoutes() {
    const [token, setToken] = useState<boolean>(false);

    // Data does't start loading
    // until *after* component is mounted
    useEffect(() => {
        const go = async () => {
            await fetch('/refresh_token_from_db', {
                method: 'POST',
                mode: 'same-origin',
                redirect: 'follow',
                credentials: 'same-origin',
                headers: {
                    'content-type': 'application/json',
                    'Accept': 'application/json'
                },
            }).then(res => res.json())
            .then(data => {
                console.log("Should run 1st", data);
                setToken(data)
            })
        }
        go()
    }, []);

    console.log("Should run 2nd", token);
    return (
        token ? <Outlet /> : <Navigate to="/login" />
    );
}

export default ProtectedRoutes
about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

If I understand you correctly, you want your component to wait for the fetch response, and then decide whether to redirect you to login or not.

In that case, add another state called isWaiting and set it initially to be true, then call setIsWaiting(false) just under setToken(data);

Finally, change the return to this:

return (
   token && !isWaiting ? <Outlet /> : <Navigate to="/login" />
);
about 4 years ago · Juan Pablo Isaza Denunciar

0

Fetching the data as well as executing the callback of an useEffect and mutating the state in React are asynchronous tasks. Which means this code:

 return (
        token ? <Outlet /> : <Navigate to="/login" />
    );

might run before, and which is happening to cause that earlier redirection. Sadly you cannot or very hardly dictate the order of execution here. One workaround is to use an additional sate, I called it checking, like so:

import { useEffect, useState } from 'react';
import { Navigate, Outlet } from 'react-router-dom';

function ProtectedRoutes() {
    const [token, setToken] = useState<boolean>(false);
    const [checking, setIsChecking] = useState<boolean>(true);
    // Data does't start loading
    // until *after* component is mounted
    useEffect(() => {
        const go = async () => {
            await fetch('/refresh_token_from_db', {
                method: 'POST',
                mode: 'same-origin',
                redirect: 'follow',
                credentials: 'same-origin',
                headers: {
                    'content-type': 'application/json',
                    'Accept': 'application/json'
                },
            }).then(res => res.json())
            .then(data => {
                console.log("Should run 1st", data);
                setToken(data)
                setIsChecking(false)
            })
        }
        go()
    }, []);

    console.log("Should run 2nd", token);
    if(checking) return <p>Loading...</p>
    return (
        token ? <Outlet /> : <Navigate to="/login" />
    );
}

export default ProtectedRoutes
about 4 years ago · Juan Pablo Isaza Denunciar

0

Issue

Your initial token state masks, or matches, the "unauthenticated" state and this is why the Navigate component is rendered on the initial render cycle and navigates to the login route.

Solution

Don't use the same initial state as either the "authenticated" or "unauthenticated" state values. Use this "indeterminant" state value to conditionally render null or some loading indicator. Also, don't mix async/await with Promise chains, it's considered anti-pattern. Keep in mind that fetch can return rejected Promises and you should handle these accordingly.

Example:

function ProtectedRoutes() {
  const [token, setToken] = useState<boolean | undefined>(); // <-- initially undefined

  // Data does't start loading
  // until *after* component is mounted
  useEffect(() => {
    const go = async () => {
      try {
        const response = await fetch('/refresh_token_from_db', {
          method: 'POST',
          mode: 'same-origin',
          redirect: 'follow',
          credentials: 'same-origin',
          headers: {
            'content-type': 'application/json',
            'Accept': 'application/json'
          },
        });
        const data = await response.json();
        setToken(data);
      } catch(error) {
        // catch/handle any errors
        // set unauthenticated status
        setToken(false);
      }
    };

    go();
  }, []);

  if (token === undefined) {
    return null; // or loading indicator/spinner/etc...
  }

  return token ? <Outlet /> : <Navigate to="/login" replace />;
}
about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda