useReducer状态在React路由切换时被重新初始化问题求助
问题分析与解决方案
问题根源
- 组件挂载触发状态重置:
Test组件每次通过路由切换进入时都会重新挂载,组件内部的useReducer会重新执行,将状态初始化为initialState(count:0),这是状态每次重置的直接原因。 - 状态作用域完全独立:
ProtectedRoutes中虽然调用了同一个Reducer,但它的状态和Test组件的状态是两个完全独立的实例,互相不共享,所以前者的状态变化不会影响后者。
解决方案
方案1:将状态提升到共享层级(推荐)
如果需要在路由切换时保留状态,或者要在多个组件间共享状态,把 useReducer 提升到路由的父组件(比如 ProtectedRoutes),通过路由的 Outlet 上下文传递状态和 dispatch:
修改 ProtectedRoutes 组件
import React from 'react' import { Navigate, useLocation, Outlet } from 'react-router-dom'; import { useAuth } from '../Context/authContext' import Header from './../Components/Header' import { Reducer, initialState } from "./../Components/Test/Reducer"; export const ProtectedRoutes = () => { const [state, dispatch] = React.useReducer(Reducer, initialState); console.log(state, "ProtectedRoutes ") const auth = useAuth(); const location = useLocation() if (auth.user && auth.user.role !== 'admin') { return <Navigate to='/' state={{ path: location.pathname }} /> } // 通过Outlet上下文传递状态和dispatch给子路由组件 return ( <div> <Header /> <Outlet context={{ reducerState: state, reducerDispatch: dispatch }} /> </div> ) }
修改 Test 组件
import React from 'react' import { useOutletContext } from 'react-router-dom'; export default function Test() { // 从父路由的Outlet获取共享的状态和dispatch const { reducerState: state, reducerDispatch: dispatch } = useOutletContext(); console.log(state, "state -----") React.useEffect(() => { // 仅在状态为初始值时触发更新,避免重复执行 if (state.count === 0) { dispatch({ type: "FETCH_SUCCESS", payload: '' }); } }, [state, dispatch]) return ( <div>index</div> ) }
方案2:状态持久化(适合组件私有状态)
如果只是 Test 组件自己的状态需要在路由切换后保留,可以将状态持久化到 localStorage,初始化时读取本地存储的值:
修改 Test 组件
import React from 'react' import { Reducer, initialState } from "./Reducer"; // 从localStorage读取初始化状态,没有则用默认值 const getInitialState = () => { const savedState = localStorage.getItem('testReducerState'); return savedState ? JSON.parse(savedState) : initialState; } export default function Test() { const [state, dispatch] = React.useReducer(Reducer, getInitialState()); // 状态变化时同步到localStorage React.useEffect(() => { localStorage.setItem('testReducerState', JSON.stringify(state)); }, [state]) React.useEffect(() => { if (state.count === 0) { dispatch({ type: "FETCH_SUCCESS", payload: '' }); } }, [state, dispatch]) console.log(state, "state -----") return ( <div>index</div> ) }
注意事项
- 方案1适合跨组件共享状态的场景,父组件在路由切换时不会卸载,因此状态不会丢失。
- 方案2适合组件私有状态的持久化,注意敏感数据不要存入
localStorage,避免安全风险。
内容的提问来源于stack exchange,提问作者Robin Thomas
相关产品推荐
相关产品推荐

