Auth este componente de autenticación y funciona bien. Excepto que no redirige si el usuario no autenticado intenta visitar /dashboard .
El backend al recibir la solicitud /api/me , conoce al usuario por tener la cookie. Entonces tengo una técnica de autenticación (Cookie-Session).
export const UserContext = createContext(); const Auth = ({ children }) => { const [user, setUser] = useState(null); const [gotUser, setGotUser] = useState(false); const navigate = useNavigate(); const getUser = async () => { const res = await fetch('/api/me'); const data = await res.json(); setUser(data); if (user) { setGotUser(true); } }; useEffect(() => { if (!gotUser) { getUser(); } }, [user, gotUser, navigate]); if (!user) { navigate('/login'); } console.log(user); return <UserContext.Provider value={user}>{children}</UserContext.Provider>; }; Entonces, el problema principal es que no se realizó la redirección. Además, el user pasado al contexto no se actualiza correctamente. Tal vez porque estoy confundido acerca de qué usar en useEffect .
Cualquier ayuda es apreciada.
Hay un par de problemas:
user .navigate se llama como un efecto secundario no intencional directamente en el cuerpo del componente. Mueva la llamada de navigate a un gancho useEffect o represente el componente Navigate para ejecutar la acción de navegación imperativa. Utilice un estado de user inicial undefined y verifíquelo explícitamente antes de ejecutar la acción de navegación o representar el componente UserContext.Provider .
const Auth = ({ children }) => { const [user, setUser] = useState(); // <-- initially undefined const navigate = useNavigate(); const getUser = async () => { try { const res = await fetch('/api/me'); const data = await res.json(); setUser(data); // <-- ensure defined, ie user object or null value } catch (error) { // handler error, set error state, etc... setUser(null); // <-- set to null for no user } }; useEffect(() => { if (user === undefined) { getUser(); } }, [user]); if (user === undefined) { return null; // <-- or loading indicator, spinner, etc } // No either redirect to log user in or render context provider and app return user ? <Navigate to="/login" replace /> : <UserContext.Provider value={user}>{children}</UserContext.Provider>; };useEffect se ejecuta después de que se procesa su JSX, por lo que a medida que se crea su código, en una página, actualice esto if (!user) que llama a navigate('/login') siempre pasará, ya que antes de que useEffect haga su trabajo, user es null , ese valor inicial que le diste a useState . Sin embargo, no está redirigiendo porque la navigate no funciona dentro de JSX, debe reemplazarse con Navigate el componente.
Además, en getUser , tiene esto if (user) solo después de setUser(data) , eso no funcionaría bien ya que user no se actualizará de inmediato, ya que actualizar un state es una tarea asíncrona que surte efecto después de un re-redner .
Para solucionar sus problemas, puede agregar un estado de checking , devolver algún cargador mientras se verifica al usuario. También puede optimizar un poco su código en general, como deshacerse de ese estado gotUser :
export const UserContext = createContext(); const Auth = ({ children }) => { const [user, setUser] = useState(null); const [checking, setChecking] = useState(true); const getUser = async () => { try { const res = await fetch("/api/me"); const data = await res.json(); setUser(data); } catch (error) { setUser(null); } finally { setChecking(false); } }; useEffect(() => { if (!user) { getUser(); } }, [user]); if (checking) { return <p>Checking...</p>; } if (!user) { return <Navigate to="/login" replace /> } return <UserContext.Provider value={user}>{children}</UserContext.Provider>; }; export default Auth;