使用redux-persist搭配redux-immutable时出现报错,请求帮助
解决 redux-immutable 与 redux-persist 配合使用的状态类型错误问题
我之前也碰到过类似的问题,核心原因是redux-persist 默认会把持久化的状态转成普通 JS 对象,但 redux-immutable 要求整个应用状态是 Immutable 集合类型,两者类型不匹配就会触发你看到的报错。下面是具体的解决思路和修改方案:
问题根源拆解
- 你用
redux-immutable的combineReducers创建了基于 Immutable Map 的根 reducer,初始状态也是 Immutable Map; - 但 redux-persist 从本地存储恢复状态时,会把序列化的普通 JS 对象直接传给 reducer,导致 reducer 收到的不是预期的 Immutable 实例,进而出现
inputState.withMutations is not a function这类类型错误。
具体修复步骤
1. 包装 react-router-redux 的 routerReducer
原生 routerReducer 只处理普通 JS 对象状态,和 redux-immutable 的 Immutable 状态不兼容,需要包装一下让它适配:
import { routerReducer } from 'react-router-redux'; import { fromJS } from 'immutable'; // 包装后让它能处理 Immutable 状态 const immutableRouterReducer = (state = fromJS({}), action) => { // 先把 Immutable 转成普通对象给原生 routerReducer 处理 const nextState = routerReducer(state.toJS(), action); // 再把结果转回 Immutable return fromJS(nextState); };
2. 自定义 redux-persist 的状态协调器
在 persistConfig 里添加 stateReconciler,把从本地存储读取的普通对象转为 Immutable Map,确保传入 reducer 的状态类型正确:
const persistConfig = { key: 'reduxStore', storage, transforms: [saveSubsetFilter, immutableTransform()], // 自定义状态转换逻辑 stateReconciler: (inboundState, originalState) => { if (!inboundState) return originalState; // 将持久化的普通对象转为 Immutable Map,同时保留原状态的 Immutable 结构 return originalState.mergeWith((prevState, inboundSubState) => { return Map.isMap(prevState) ? Map(inboundSubState) : inboundSubState; }, Map(inboundState)); }, };
3. 确保子 reducer 兼容普通对象输入
每个子 reducer 要能接收普通对象作为初始状态,并自动转为 Immutable 类型,比如 Login 子 reducer:
import { Map } from 'immutable'; const initialState = Map({ userData: Map() }); export default (state = initialState, action) => { // 如果传入的是普通对象,先转为 Immutable Map if (!Map.isMap(state)) { state = Map(state); } switch(action.type) { // 你的业务逻辑 default: return state; } };
修改后的完整 store.js
import { createStore, applyMiddleware } from 'redux'; import { composeWithDevTools } from 'remote-redux-devtools'; import logger from 'redux-logger'; import { createEpicMiddleware } from 'redux-observable'; import immutableTransform from 'redux-persist-transform-immutable'; import { persistStore, persistReducer } from 'redux-persist'; import { combineReducers } from 'redux-immutable'; import { Map, fromJS } from 'immutable'; import { routerReducer } from 'react-router-redux'; import storage from 'redux-persist/lib/storage'; import createFilter from 'redux-persist-transform-filter'; import epics from './epics'; import reducers from './reducers'; // 适配 Immutable 的路由 reducer const immutableRouterReducer = (state = fromJS({}), action) => { const nextState = routerReducer(state.toJS(), action); return fromJS(nextState); }; const middlewares = [createEpicMiddleware(epics), logger]; const composeEnhancers = composeWithDevTools({ realtime: true, port: 8000 }); const saveSubsetFilter = createFilter( 'Login', ['userData'], ); const persistConfig = { key: 'reduxStore', storage, transforms: [saveSubsetFilter, immutableTransform()], stateReconciler: (inboundState, originalState) => { if (!inboundState) return originalState; return originalState.mergeWith((prevState, inboundSubState) => { return Map.isMap(prevState) ? Map(inboundSubState) : inboundSubState; }, Map(inboundState)); }, }; const initialState = Map(); const persistedReducer = persistReducer(persistConfig, combineReducers({ ...reducers, routing: immutableRouterReducer, // 使用包装后的路由 reducer })); export const store = createStore( persistedReducer, initialState, composeEnhancers(applyMiddleware(...middlewares)), ); export const persistor = persistStore(store);
额外注意事项
- 检查依赖版本兼容性:确保
redux-persist(建议 v6+)和redux-immutable(建议 v4+)版本匹配,避免因版本差异导致的隐性问题; - 如果使用了其他第三方 reducer(比如 form 类库的 reducer),也要做类似 Immutable 适配处理,保证整个状态树都是 Immutable 类型。
内容的提问来源于stack exchange,提问作者Soroush Chehresa
相关产品推荐
相关产品推荐

