React/Strapi应用登录后useNavigate致Auth未持久化到LocalStorage
问题:React/Strapi认证登录后跳转导致LocalStorage未写入的问题
我正在重构React/Strapi Web应用的认证模块,应用内部分内容需登录后才可访问。登录逻辑为向Strapi后端提交凭证,将返回的JWT存储至LocalStorage。我编写了useAuth自定义Hook负责认证状态的设置、持久化与提供,它会检查本地是否存储了auth对象,存在则返回,否则初始化状态,该Hook被登录表单组件调用。
当前问题:登录成功后使用react-router-dom的useNavigate钩子跳转到/dashboard页面时,出现以下情况:
- 页面刷新
- Auth数据未写入LocalStorage
- 用户无法访问受保护内容
- 登录表单被清空
若注释掉navigate("/dashboard")语句,Auth可正确写入LocalStorage,用户停留在登录页(表单清空)且能正常访问受保护页面。怀疑是useNavigate使用错误,或与useAuth Hook存在未考虑的交互。
useAuth Hook代码
import { useEffect, useState } from "react"; const AUTH_STORAGE_KEY = "my-app-auth"; export const useAuth = () => { const [auth, setAuth] = useState(() => { try { const storedAuth = localStorage.getItem(AUTH_STORAGE_KEY); return storedAuth ? JSON.parse(storedAuth) : { user: null, accessToken: null }; } catch (error) { console.error("Error parsing stored auth:", error); return { user: null, accessToken: null }; } }); useAuthPersistence(auth); return { auth, setAuth }; }; export const useAuthPersistence = (auth) => { useEffect(() => { try { if (auth.accessToken) { localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(auth)); } else { localStorage.removeItem(AUTH_STORAGE_KEY); } } catch (error) { console.error("Error persisting auth:", error); } }, [auth, AUTH_STORAGE_KEY]); };
登录表单组件相关代码
import React, { useRef, useState, useEffect } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useAuth } from '../../hooks/useAuth.js'; export function SignInForm () { const userRef = useRef(); const { setAuth } = useAuth(); const navigate = useNavigate(); // [部分代码省略] const handleSubmit = async (e) => { // [部分代码省略] if(response.ok) { const data = await response.json(); const user = data.user; const accessToken = data.jwt; console.log(user) console.log(accessToken) // Store the user and access token in the local storage setAuth({ user, accessToken }); // Navigate to the dashboard navigate("/dashboard"); } else if (response.status === 400) { // [部分代码省略] }
问题原因与解决方案
核心原因
setAuth是React异步状态更新函数,调用后立刻执行navigate会导致状态还未更新完成,useAuthPersistence的useEffect还没触发写入LocalStorage,新页面加载时读取不到认证数据。- 表单提交未阻止默认行为,导致浏览器发起传统同步提交,直接刷新页面,状态来不及持久化就被重置。
解决步骤
阻止表单默认提交行为
在handleSubmit开头添加代码,避免页面刷新:const handleSubmit = async (e) => { e.preventDefault(); // 阻止浏览器默认表单提交 // 原有请求逻辑... };等待状态更新完成后再跳转
通过useEffect监听auth状态变化,确认accessToken存在后再执行跳转:export function SignInForm () { const userRef = useRef(); const { auth, setAuth } = useAuth(); // 新增auth状态引用 const navigate = useNavigate(); // 监听auth状态,认证成功后跳转 useEffect(() => { if (auth.accessToken) { navigate("/dashboard"); } }, [auth, navigate]); const handleSubmit = async (e) => { e.preventDefault(); // 原有请求逻辑... if(response.ok) { const data = await response.json(); const user = data.user; const accessToken = data.jwt; setAuth({ user, accessToken }); // 移除原有直接调用的navigate } // 错误处理逻辑... }; // 其他组件代码... }优化
useAuthPersistence依赖项
移除常量AUTH_STORAGE_KEY的依赖,避免不必要的useEffect触发:export const useAuthPersistence = (auth) => { useEffect(() => { try { if (auth.accessToken) { localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(auth)); } else { localStorage.removeItem(AUTH_STORAGE_KEY); } } catch (error) { console.error("Error persisting auth:", error); } }, [auth]); // 移除AUTH_STORAGE_KEY };
内容的提问来源于stack exchange,提问作者Feldmanovitch
相关产品推荐
相关产品推荐

