React+GraphQL+Apollo多用户切换后,无刷新获取新用户信息问题
问题描述
我使用React、GraphQL、Apollo Client、MongoDB Atlas开发一款语录应用,登录时将认证token存储在localStorage中,登出时移除该token。应用内的个人资料页用于展示当前登录用户的详情,但遇到一个问题:用户登出后切换登录另一个账号时,页面仍显示前一位用户的详情,只有刷新页面后才会显示当前登录用户的信息,需要实现无需刷新页面即可获取新用户详情的效果。
问题根源
Apollo Client默认会缓存查询结果,登出操作仅移除了localStorage中的token,但未清除缓存里的旧用户数据;新用户登录时,如果没有主动触发缓存更新或重新查询,组件会继续读取缓存中的旧数据,导致页面展示异常。
解决方案
通过清理Apollo缓存、主动触发查询刷新、优化组件查询逻辑三个方向解决:
- 登出时重置Apollo缓存,彻底清除旧用户数据
- 登录成功后主动刷新用户资料查询,更新缓存
- 优化Profile组件的查询触发时机,确保路由切换时获取最新数据
修改后的关键代码
NavBar组件(优化登出逻辑)
import React from "react"; import { useNavigate } from "react-router"; import { Link } from "react-router-dom"; import { client } from "../index.js"; function NavBar() { const token = localStorage.getItem("token"); const navigate = useNavigate(); return ( <div> <nav> <div className="nav-wrapper"> <Link to="/" className="brand-logo left"> Quotes </Link> <ul id="nav-mobile" className="right"> {token ? ( <> <li> <Link to="/profile">Profile</Link> </li> <li> <Link to="/create">Create</Link> </li> <li> <button className="red btn" onClick={async() => { localStorage.removeItem("token"); await client.resetStore(); navigate("/login"); }} > Logout </button> </li> </> ) : ( <> <li> <Link to="/login">Login</Link> </li> <li> <Link to="/signup">Signup</Link> </li> </> )} </ul> </div> </nav> </div> ); } export default NavBar;
Login组件(优化登录后缓存更新)
import { useMutation } from "@apollo/client"; import React, { useState } from "react"; import { useNavigate } from "react-router"; import { Link } from "react-router-dom"; import { LOGIN_USER } from "../gqloperations/mutations"; export default function Login() { const [formData, setFormData] = useState({ email: "", password: "" }); const [signinUser, { loading, data, error, client }] = useMutation(LOGIN_USER, { refetchQueries: ["getMyProfile"], onCompleted(data) { localStorage.setItem("token", data.user.token); navigate("/"); }, }); const navigate = useNavigate(); const handleChange = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }); }; const handleSubmit = (e) => { e.preventDefault(); signinUser({ variables: { userSignin: formData, }, }); setFormData({ email: "", password: "" }); }; if (loading) return <h1>Loading</h1>; return ( <> {error && <div className="red card-panel">{error.message}</div>} {data && ( <div className="green card-panel"> Login successfully </div> )} <div> <h1>Login</h1> <form onSubmit={handleSubmit}> <input type="email" name="email" value={formData.email} onChange={handleChange} placeholder="email" /> <input type="password" name="password" value={formData.password} onChange={handleChange} placeholder="password" /> <Link to="/signup"> <p>Don't have an account ?</p> </Link> <button type="submit" className="green btn"> Login </button> </form> </div> </> ); }
Profile组件(优化查询触发时机)
import React, { useEffect } from "react"; import { useQuery } from "@apollo/client"; import { GET_MY_PROFILE } from "../gqloperations/queries"; import { useNavigate } from "react-router"; export default function Profile() { const { loading, error, data, refetch } = useQuery(GET_MY_PROFILE); const navigate = useNavigate(); // 路由进入时重新查询,确保获取最新用户数据 useEffect(() => { if (localStorage.getItem("token")) { refetch(); } else { navigate("/login"); } }, [navigate]); if (loading) return <h1>Loading...</h1>; if (error) console.log(error); return ( <div className="container"> <div className="center-align"> <img style={{ border: "2px solid black", marginTop: "10px" }} className="circle" src={`https://robohash.org/${data.user.firstName}.png?size=200x200`} alt="" /> <h5> {data.user.firstName} {data.user.lastname} </h5> <h5>Email-{data.user.email}</h5> </div> <h3>Your quotes</h3> {data.user.quotes.map((quote) => { return ( <blockquote key={quote._id}> <h6>{quote.name}</h6> </blockquote> ); })} </div> ); }
关键修改说明
- 登出时调用
client.resetStore():清空Apollo缓存的同时重新执行所有活跃查询,彻底清除旧用户数据,避免新登录时读取缓存 - 登录时保留
refetchQueries: ["getMyProfile"]:登录成功后主动触发用户资料查询,更新缓存中的用户数据 - Profile组件调整useEffect逻辑:在组件挂载/路由切换时检查token并重新查询,确保每次进入个人资料页都获取最新用户信息
内容的提问来源于stack exchange,提问作者front_dev_j
相关产品推荐
相关产品推荐

