React+React-Router6+Redux-Toolkit页面刷新/跳转后state丢失问题
Redux状态丢失问题:页面刷新/跳转后状态重置
我用React、React-Router 6和Redux-Toolkit练手时碰到个问题——页面刷新或者跳转到其他路由后,Redux里的状态就没了,控制台还会显示之前页面的state。
相关代码片段
Post.js 组件代码
import React, { useEffect } from 'react' import { useNavigate, useParams } from 'react-router-dom' import jwtDecode from 'jwt-decode'; import { useSelector, useDispatch } from 'react-redux'; import { getPostAsync, postsSelector, deletePostAsync } from '../redux/slices/postSlice'; const Post = () => { const { post } = useSelector(postsSelector); const { postId } = useParams(); const navigate = useNavigate(); const dispatch = useDispatch(); useEffect(() => { dispatch(getPostAsync(postId)); // 视频中控制台日志的来源 console.log(post); }, [dispatch, postId]); const handleDelete = () => { dispatch(deletePostAsync(postId)); navigate('/posts') } const handleUpdate = () => { navigate(`/posts/${postId}/update-post`) } //解码token以获取用户名属性,将其指定为帖子作者 const token = localStorage.getItem('user'); const decode = jwtDecode(token); return ( <div> <h1>{post.title}</h1> <h2>{post.body}</h2> <p>Author: {post.author}</p> {decode._id === post.user_id && <div className='button-group'> <button onClick={handleDelete}>Delete</button> <button onClick={handleUpdate}>Update</button> </div>} </div> ) } export default Post
Redux Slice 初始状态
export const postSlice = createSlice({ name: 'posts', initialState: { posts: [], post: {}, loading: false, error: false } })
Redux 同步reducer(getPost)
getPost: (state, action) => { state.post = action.payload; }
异步获取帖子逻辑(getPostAsync)
export const getPostAsync = (postId) => async (dispatch) => { try { const response = await axiosWithAuth().get(`api/posts/${postId}`); dispatch(getPost(response.data)); } catch (error) { dispatch(setError(error)); } }
问题原因与解决办法
核心原因
Redux状态默认只存在内存中:页面刷新时浏览器重启JS环境,内存里的状态会被清空,直接回到Slice定义的初始状态;路由跳转时,若新页面未及时触发数据请求,会暂时显示旧的state,直到新请求完成。
具体修复步骤
页面刷新时的状态持久化
把Redux状态存到localStorage里,推荐用redux-persist库快速实现:- 安装依赖:
npm install redux-persist - 修改Redux store配置:
// store.js import { configureStore } from '@reduxjs/toolkit'; import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; // 默认用localStorage import { postSlice } from './slices/postSlice'; const persistConfig = { key: 'root', storage, whitelist: ['posts'] // 指定要持久化的slice }; const persistedReducer = persistReducer(persistConfig, postSlice.reducer); export const store = configureStore({ reducer: { posts: persistedReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, // 关闭序列化检查避免警告 }), }); export const persistor = persistStore(store); - 根组件用
PersistGate包裹:// index.js import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import { store, persistor } from './redux/store'; ReactDOM.createRoot(document.getElementById('root')).render( <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <App /> </PersistGate> </Provider> );
- 安装依赖:
路由跳转后的状态更新优化
原代码中useEffect里直接console.log(post)会打印旧值,因为异步请求还没完成,要监听post变化再打印:// 替换原有的console.log逻辑 useEffect(() => { console.log(post); }, [post]);另外,你的
useEffect已经把postId加入依赖数组,路由跳转后postId变化时会自动重新发起请求,这部分逻辑没问题。避免空值渲染
组件渲染时先判断post是否有数据,防止出现空内容:return ( <div> {!post.title ? <div>加载中...</div> : ( <> <h1>{post.title}</h1> <h2>{post.body}</h2> <p>Author: {post.author}</p> {decode._id === post.user_id && <div className='button-group'> <button onClick={handleDelete}>Delete</button> <button onClick={handleUpdate}>Update</button> </div>} </> )} </div> )
内容的提问来源于stack exchange,提问作者Gerald Thomas
相关产品推荐
相关产品推荐

