React-router-DOM切换页面时Context状态重置问题求助
问题描述
我已经折腾好几个小时了,还是没找到问题的解决办法。我正在开发一个用React Context的应用,同时用react-router-dom做页面导航。Context里存了初始状态,用useReducer Hook修改状态,但切换页面时状态会重新加载,必须重新发GET请求才能拿到数据。看到资料说react-router-dom会导致页面刷新,所以状态没法保留。求各位给点解决建议,实在想不出怎么让状态“持久化”。
相关代码
index.js代码
import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import { AppProvider } from "./context"; import "./styles.css"; import Login from "./pages/Login"; import Profile from "./pages/Profile"; import Register from "./pages/Register"; import FriendshipAccess from "./pages/FriendshipAccess"; import SearchPage from "./pages/SearchPage"; import EditAccount from "./pages/EditAccount"; ReactDOM.render( <React.StrictMode> <AppProvider> <BrowserRouter> <Routes> <Route exact path="/" element={<Login />} /> <Route path="/profile" element={<Profile />} /> <Route path="/account/edit" element={<EditAccount />} /> <Route path="/register" element={<Register />} /> <Route path="/users/:nickname" element={<FriendshipAccess />} /> <Route path="/users/search" element={<SearchPage />} /> </Routes> </BrowserRouter> </AppProvider> </React.StrictMode>, document.getElementById("root") );
Context代码
import React, { useContext, useReducer } from "react"; import reducer from "./reducer"; import { registerNewUser, authUser, getUserPosts, accessUserProfile, validateUserToken, editAccountData, } from "./operations/operations"; const AppContext = React.createContext(); const initialState = { userState: { userData: {}, userPosts: [], }, serverMessages: {}, accessToken: false, accessedUserPage: { userData: {}, userPosts: {}, }, }; const AppProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); const registerSubmitHandler = async (userInputs) => { const registeredUser = await registerNewUser(userInputs); dispatch({ type: "REGISTER_USER", payload: registeredUser.data }); }; const authUserCredentials = async (userCredentials) => { const userDataFromDb = await authUser(userCredentials); localStorage.setItem("access_token", userDataFromDb.token); dispatch({ type: "AUTH_USER", payload: userDataFromDb, }); }; const getUserDataByToken = async (token) => { const userDataFromDb = await validateUserToken(token); dispatch({ type: "LOGGED_GET_DATA", payload: userDataFromDb }); }; const editUserAccount = async (token, accountData) => { const editedUserData = await editAccountData(token, accountData); dispatch({ type: "EDIT_USER_ACCOUNT", payload: editedUserData }); }; const getUserPostsData = async (token) => { const postsDataFromDb = await getUserPosts(token); dispatch({ type: "FETCH_USER_POSTS", payload: postsDataFromDb }); }; const getBuddyData = async (nicknameParam) => { const userDataFromDb = await accessUserProfile(nicknameParam); dispatch({ type: "ACCESS_FRIEND_PAGE", payload: userDataFromDb }); }; const userLogout = async () => { await localStorage.removeItem("access_token"); const emptyState = { ...state, userState: { userData: "", userPosts: "", }, }; dispatch({ type: "USER_LOGOUT", payload: emptyState }); }; return ( <AppContext.Provider value={{ ...state, registerSubmitHandler, authUserCredentials, getUserPostsData, userLogout, getBuddyData, getUserDataByToken, editUserAccount, }} > {children} </AppContext.Provider> ); }; export const useGlobalContext = () => { return useContext(AppContext); }; export { AppProvider, AppContext };
解决建议
检查页面跳转方式:必须使用react-router-dom提供的
<Link>组件或useNavigate钩子进行页面跳转,禁止使用原生<a>标签。原生a标签会触发整页刷新,导致React应用重新挂载,Context状态被重置。- 错误写法:
<a href="/profile">个人主页</a> - 正确写法:
<Link to="/profile">个人主页</a>或者通过钩子调用:const navigate = useNavigate(); navigate('/profile')
- 错误写法:
从localStorage恢复并持久化状态:利用localStorage保存状态数据,初始化时读取已有状态,避免每次刷新都回到初始值。
- 修改AppProvider中useReducer的初始化逻辑:
// 从localStorage读取保存的状态,无数据则使用初始状态 const savedState = localStorage.getItem('appState'); const initialStateWithPersist = savedState ? JSON.parse(savedState) : initialState; const [state, dispatch] = useReducer(reducer, initialStateWithPersist); - 添加useEffect监听状态变化,实时保存到localStorage:
注意:若状态包含敏感信息,建议改用sessionStorage或加密存储,降低安全风险。import { useEffect } from 'react'; // 需要导入useEffect // 在AppProvider组件内添加 useEffect(() => { // 仅保存状态中的数据部分,排除函数 const persistableState = { userState: state.userState, serverMessages: state.serverMessages, accessToken: state.accessToken, accessedUserPage: state.accessedUserPage }; localStorage.setItem('appState', JSON.stringify(persistableState)); }, [state]);
- 修改AppProvider中useReducer的初始化逻辑:
优化组件数据获取逻辑:在需要数据的组件中,先判断Context内是否已有数据,仅在数据缺失时发起请求。以Profile组件为例:
const { userState, getUserPostsData, accessToken } = useGlobalContext(); useEffect(() => { // 仅当用户帖子为空且存在token时,才发起请求 if (!userState.userPosts.length && accessToken) { getUserPostsData(accessToken); } }, [userState.userPosts, accessToken, getUserPostsData]);确认Provider层级正确性:当前index.js中
AppProvider包裹BrowserRouter的结构是正确的,保持该层级,确保所有路由组件都能访问到Context状态。
内容的提问来源于stack exchange,提问作者Lucas Rocha
相关产品推荐
相关产品推荐

