React Context实现用户登录时的状态持久化与重渲染问题
React Context虚拟用户登录状态跳转后丢失问题
我用React Context实现虚拟用户登录功能时遇到了问题:切换虚拟用户登录后,跳转页面Context中的用户状态无法保持,我怀疑是组件重渲染导致的。
相关资源
- 问题演示Gif:直观展示状态丢失的过程
- GitHub仓库:nc-news-frontend
- 在线演示:nc-news-roshan.netlify.app
当前项目相关代码
UserContext实现
import { createContext, useState } from "react"; export const UserContext = createContext(); export const UserProvider = (props) => { const [user, setUser] = useState("grumpy19"); return ( <UserContext.Provider value={{ user, setUser }}> {props.children} </UserContext.Provider> ); };
Users组件
import { useEffect, useState } from "react"; import { getUsers } from "../../utils/utils"; import { UserContext } from "../../contexts/UserContent"; import { useContext } from "react"; import "./Users.css"; import Spinner from "react-bootstrap/Spinner"; import { Card, CardGroup } from "react-bootstrap"; import { Badge } from "react-bootstrap"; import Stack from "react-bootstrap/Stack"; export default function Users() { const [users, setUsers] = useState([]); const [isLoading, setIsLoading] = useState(false); const [err, setErr] = useState(null); const { setUser, user } = useContext(UserContext); function handleAvatarClick(username) { setUser(username); } useEffect(() => { setIsLoading(true); setErr(false); getUsers() .then(({ users }) => { setUsers(users); setIsLoading(false); }) .catch((err) => { setIsLoading(false); setErr(err); }); }, []); if (err) return ( <div> <h1>Whoops</h1> <h2>Status: {err.status}</h2> <h2>{err.msg}</h2> </div> ); return ( <div className="user-container"> <h2 id="user-heading">Users</h2> <p id="user-desc">Click on a users image to log in</p> <p id="current-user"> Hello <Badge bg="dark">{user}</Badge> ! </p> {isLoading ? ( <Spinner animation="border" variant="primary" className="spinner" /> ) : null} <CardGroup id="users-list"> <ul> {users.map((person, index) => { return ( <Card key={index} className="user-card" onClick={() => { handleAvatarClick(person.username); }} > <Stack className="user-info" direction="horizontal" gap={3}> <div className="p-2">{person.name}</div> <div className="p-2 ms-auto"> {" "} <Badge>{person.username}</Badge> </div> </Stack> <Card.Img src={person.avatar_url} /> </Card> ); })} </ul> </CardGroup> </div> ); }
之前正常工作的项目代码
UserContext实现
import { createContext, useState } from "react"; export const UserContext = createContext(); export const UserProvider = (props) => { const [user, setUser] = useState("Paul-R"); return ( <UserContext.Provider value={{ user, setUser }}> {props.children} </UserContext.Provider> ); };
Users组件
import { useEffect, useState } from "react"; import "./Users.css"; import { getUsers } from "../../utils.js/api"; import { UserContext } from "../../contexts/UserContent"; import { useContext } from "react"; export default function Users() { const [users, setUsers] = useState([]); const [isLoading, setIsLoading] = useState(false); const [err, setErr] = useState(null); const { setUser } = useContext(UserContext); function handleAvatarClick(username) { setUser(username); } useEffect(() => { setIsLoading(true); setErr(false); getUsers() .then(({ users }) => { setUsers(users); setIsLoading(false); }) .catch((err) => { setIsLoading(false); setErr(err); }); }, []); if (isLoading) return <p>Loading...</p>; if (err) return ( <div> <h1>Whoops</h1> <h2>Status: {err.status}</h2> <h2>{err.msg}</h2> </div> ); return ( <div> <h1>Users</h1> <p>Click on a users image to log in</p> <ul> {users.map((user) => { return ( <li key={user.username}> <p>username: {user.username}</p> <p>kudos: {user.kudos}</p> <img src={user.avatar_url} alt="" width="100" onClick={() => { handleAvatarClick(user.username); }} /> </li> ); })} </ul> </div> ); }
问题原因与解决方案
核心原因
React Context的状态在组件树内部重渲染时是能保持的,但如果页面跳转是硬刷新(比如用原生<a>标签而非React Router的<Link>),会导致整个应用重新加载,useState的初始值会重置为默认的"grumpy19",这就是状态丢失的根本原因。
对比你之前的项目,大概率是之前使用了React Router的客户端路由跳转,而当前项目用了触发页面刷新的跳转方式。
解决方案
改用React Router客户端跳转:确保页面跳转使用React Router提供的
<Link>组件,避免原生<a>标签带来的硬刷新。添加状态持久化:将用户状态同步到
localStorage,即使硬刷新也能恢复状态,修改UserContext代码如下:
import { createContext, useState, useEffect } from "react"; export const UserContext = createContext(); export const UserProvider = (props) => { // 初始化时优先从localStorage读取,无值则用默认值 const [user, setUser] = useState(() => localStorage.getItem("currentUser") || "grumpy19"); useEffect(() => { // 状态变化时同步到localStorage localStorage.setItem("currentUser", user); }, [user]); return ( <UserContext.Provider value={{ user, setUser }}> {props.children} </UserContext.Provider> ); };
- 检查导入路径:当前代码中存在
import { UserContext } from "../../contexts/UserContent";,注意路径中的文件名拼写是否正确(UserContentvsUserContext),避免因导入错误引发的状态异常。
内容的提问来源于stack exchange,提问作者Roshan R
相关产品推荐
相关产品推荐

