Formik值存入Redux Persist切片后跳转刷新丢失问题
问题:Redux Persist 存储表单草稿在跨页面刷新后丢失
我尝试把Formik表单的值保存到Redux Persist切片里,在表单页面刷新时一切正常,但跳转到其他页面刷新后,返回表单页面发现切片数据为空。
问题重现步骤
- 在表单中修改值,切片被正常填充
- 跳转到其他页面
- 刷新该页面
- 返回表单页面,切片数据为空
相关代码
Store 配置
import { configureStore } from "@reduxjs/toolkit"; //SLICES import mainLayoutReducer from "./mainLayoutSlice"; import userDataReducer from "./userDataSlice"; import schedulePeriodReducer from "./schedulePeriodSlice"; import loadingReducer from "./loadingSlice"; import draftDataSlice from "./draftDataSlice"; // API import { tmsApi } from "../api"; import { daDataApi } from "../api/thirdPartyApis/daDataApi"; import { persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, } from "redux-persist"; import storage from "redux-persist/lib/storage"; const persistConfig = { key: "root", storage, }; const persistedMainLayoutReducer = persistReducer( persistConfig, mainLayoutReducer ); const persistedDraftReducer = persistReducer( persistConfig, draftDataSlice ); const persistedLoadingReducer = persistReducer(persistConfig, loadingReducer); const store = configureStore({ reducer: { // PERSISTED SLICES mainLayout: persistedMainLayoutReducer, loading: persistedLoadingReducer, draftData: persistedDraftReducer, // schedulePeriod: persistedSchedulePeriodReducer, // SLICES userData: userDataReducer, schedulePeriod: schedulePeriodReducer, // API [tmsApi.reducerPath]: tmsApi.reducer, [daDataApi.reducerPath]: daDataApi.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }) .concat(tmsApi.middleware) .concat(daDataApi.middleware), }); export default store;
Draft 切片
import { createSlice } from "@reduxjs/toolkit"; const initialState = { draftData: null, }; export const draftDataSlice = createSlice({ name: "draftData", initialState, reducers: { setDraftData: (state, action) => { state.draftData = action.payload; }, clearDraftData: (state) => { state.draftData = null; }, }, }); export const { setDraftData, clearDraftData } = draftDataSlice.actions; export default draftDataSlice.reducer;
表单组件
const isDraft = location.search; const draftData = useSelector((state) => state.draftData.draftData); const formik = useFormik({ initialValues: isDraft ? draftData : { ...mainInfoValues(employeeInfo), ...vehicleInfoValues(employeeInfo), ...docsValues(employeeInfo), }, onSubmit: // here onSubmit func }) useEffect(() => { dispatch(setDraftData(values)); }, [values]); return( <form onSubmit={handleSubmit}> <Section overflow="inherit"> {(() => { switch (step) { case 1: return ( <MainInfo values={values} handleChange={handleChange} setFieldValue={setFieldValue} setValues={setValues} touched={touched} errors={errors} handleBlur={handleBlur} isStepOneEmpty={isStepOneEmpty} resetForm={resetForm} handleStep={handleStep} /> ); case 2: return ( <VehicleInfo values={values} handleChange={handleChange} setFieldValue={setFieldValue} setValues={setValues} touched={touched} errors={errors} handleBlur={handleBlur} isStepTwoEmpty={isStepTwoEmpty} resetForm={resetForm} handleStep={handleStep} /> ); case 3: return ( <AttachDocs values={values} handleChange={handleChange} setFieldValue={setFieldValue} setValues={setValues} touched={touched} errors={errors} handleBlur={handleBlur} isStepThreeEmpty={isStepThreeEmpty} resetForm={resetForm} handleStep={handleStep} /> ); default: break; } })()} </Section> </form> )
问题原因与解决方案
核心问题
你给每个需要持久化的切片都使用了同一个persistConfig(key为"root"),这会导致多个持久化 reducer 互相覆盖存储的数据。Redux Persist 中,每个持久化 reducer 的存储key必须唯一,或者采用根持久化的方式统一管理。
解决方法
方法1:根持久化(推荐)
只对整个根 reducer 做持久化,统一管理所有需要存储的切片:
修改 Store 配置:
import { configureStore, combineReducers } from "@reduxjs/toolkit"; import { persistStore, persistReducer } from "redux-persist"; import storage from "redux-persist/lib/storage"; //SLICES import mainLayoutReducer from "./mainLayoutSlice"; import userDataReducer from "./userDataSlice"; import schedulePeriodReducer from "./schedulePeriodSlice"; import loadingReducer from "./loadingSlice"; import draftDataSlice from "./draftDataSlice"; // API import { tmsApi } from "../api"; import { daDataApi } from "../api/thirdPartyApis/daDataApi"; import { FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, } from "redux-persist"; // 合并需要持久化的reducer const rootReducer = combineReducers({ mainLayout: mainLayoutReducer, loading: loadingReducer, draftData: draftDataSlice, userData: userDataReducer, schedulePeriod: schedulePeriodReducer, }); const persistConfig = { key: "root", storage, // 可选:只持久化指定切片,注释则默认全部持久化 // whitelist: ["mainLayout", "loading", "draftData"] }; const persistedReducer = persistReducer(persistConfig, rootReducer); const store = configureStore({ reducer: { persisted: persistedReducer, // API reducer 无需持久化 [tmsApi.reducerPath]: tmsApi.reducer, [daDataApi.reducerPath]: daDataApi.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }) .concat(tmsApi.middleware) .concat(daDataApi.middleware), }); export const persistor = persistStore(store); export default store;
在应用入口用PersistGate包裹根组件:
import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import store, { persistor } from './store'; ReactDOM.render( <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <App /> </PersistGate> </Provider>, document.getElementById('root') );
注意:修改后,状态获取路径需要调整,比如原来的state.draftData.draftData要改为state.persisted.draftData.draftData。
方法2:给每个切片设置唯一存储key
如果坚持单独持久化每个切片,需为每个切片的persistConfig设置不同的key:
// DraftData 专属配置 const draftPersistConfig = { key: "draftData", storage, }; const persistedDraftReducer = persistReducer(draftPersistConfig, draftDataSlice); // MainLayout 专属配置 const mainLayoutPersistConfig = { key: "mainLayout", storage, }; const persistedMainLayoutReducer = persistReducer(mainLayoutPersistConfig, mainLayoutReducer); // Loading 同理设置唯一key
这种方式不需要调整状态路径,但每个切片会单独存储在localStorage中,管理成本较高。
额外检查点
- 确保Formik的
values是可序列化的:如果包含函数、Symbol等不可序列化数据,Redux Persist无法存储,会导致数据丢失,可在setDraftData前过滤或转换这些数据。 - 查看浏览器localStorage:打开开发者工具 > Application > LocalStorage,确认存储项是否存在且数据正确。
内容的提问来源于stack exchange,提问作者David Abramov
相关产品推荐
相关产品推荐

