Next.js Strapi Apollo GraphQL认证跳转与状态更新失效问题
Next.js + Strapi + GraphQL 认证功能问题修复
核心问题定位
- 登录页跳转失效:页面内监听用户状态触发跳转的
useEffect依赖数组为空,仅在组件首次挂载时执行一次。登录请求返回更新Context中的用户状态后,钩子不会重新触发,因此router.push不会执行。 - 页面刷新/业务页状态检测失效:全局用户Context未做持久化同步,页面刷新后Context状态重置为初始值,不会自动读取Cookie中存储的JWT令牌恢复登录态;同时部分
useEffect依赖声明不全,存在闭包陷阱导致拿到旧状态值。 - 隐性逻辑冲突:登出逻辑先执行路由跳转再清空用户状态,跳转过程中路由守卫检测到未清空的用户状态会出现逻辑判断错误;本地图片引入、JSX属性写法存在不规范问题会触发控制台警告。
分步修复方案
1. 修复登录页跳转逻辑
修改登录页监听用户状态的useEffect,补全依赖项,同时添加路由参数防止登录后回退到登录页:
// 原代码依赖为[],修改为[user, router] useEffect(() => { if (user !== "" && user !== undefined) { router.push("/account", undefined, { replace: true }); } }, [user, router]);
更稳妥的方式是直接在登录Mutation的完成回调里处理状态更新和跳转,避免依赖状态同步的延迟:
const [loginUser, { loading, error, data }] = useMutation(LOGIN_USER, { onCompleted: (loginData) => { const userInfo = { token: loginData.login.jwt, username: loginData.login.user.username, id: loginData.login.user.id, email: loginData.login.user.email, } // 更新全局状态 dispatchUser(setUserAction(userInfo, SIGN_IN)); // 持久化存储:JWT存Cookie,用户信息存localStorage document.cookie = `jwt=${loginData.login.jwt}; path=/; max-age=86400; SameSite=Lax`; localStorage.setItem('userInfo', JSON.stringify(userInfo)); // 跳转至账号页 router.push("/account", undefined, { replace: true }); } }); // 删除原代码中监听data变化的useEffect,避免重复执行逻辑
2. 修复全局Context持久化逻辑
在UserWrapper上下文组件中添加初始化逻辑,页面加载时自动读取本地存储恢复登录态:
// 在UserWrapper组件内部添加初始化钩子 useEffect(() => { // 封装Cookie读取方法 const getCookie = (name) => { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); return null; }; const token = getCookie('jwt'); const savedUser = localStorage.getItem('userInfo'); // Cookie存在有效令牌时恢复全局状态 if (token && savedUser) { dispatchUser(setUserAction(JSON.parse(savedUser), SIGN_IN)); } }, []);
同时在Context的reducer中补充状态清理逻辑:触发SIGN_OUT动作时,同步清除Cookie和localStorage中存储的登录信息。
3. 修复业务页路由守卫与登出逻辑
修改账号页的状态检测useEffect,补全依赖项避免闭包问题;调整登出逻辑执行顺序,先清状态再跳转:
// 补全useEffect依赖数组 useEffect(() => { if (user) { getUserOrders({ variables: { filters: { user: { id: { eq: user.id } }, status: { ne: "rejected" }, }, }, }); } else { router.push("/account/login", undefined, { replace: true }); } }, [user, router, getUserOrders]); // 调整登出逻辑顺序 const handleLogout = () => { // 先清空全局状态、本地存储 dispatchUser(setUserAction(null, SIGN_OUT)); document.cookie = "jwt=; path=/; max-age=0"; localStorage.removeItem('userInfo'); // 最后跳转至登录页 router.push("/account/login", undefined, { replace: true }); };
4. 其他不规范问题修复
- 本地静态图片不要用
require引入,直接在文件顶部导入:import logo from "../../public/Img/logo.png",Image组件src属性直接传入导入的logo变量即可 - JSX中label的
for属性替换为htmlFor,避免和JS保留字冲突触发React警告 - 密码输入框的type属性从
text改为password,防止输入内容明文显示 - 补全代码中缺失的
useLazyQuery、图标组件、常量等导入,避免编译报错
验证标准
修复后按以下流程验证功能:
- 输入正确账号密码提交登录,成功后自动跳转至账号个人页,点击浏览器后退按钮不会回到登录页
- 登录状态下刷新任意业务页面,不会自动跳转到登录页,用户信息、订单数据正常加载
- 点击登出按钮后,登录状态被清空,自动跳转至登录页,此时直接访问/account路由会被拦截跳回登录页
- 手动在浏览器中写入有效JWT Cookie后访问站点,会自动识别登录态进入对应页面,无需重复登录
内容的提问来源于stack exchange,提问作者Dilshod Sh
相关产品推荐
相关产品推荐

