使用Next.js+Immutable.js+Redux Saga时getServerSideProps序列化initialState.ui失败及服务器端无法dispatch动作问题
我来帮你拆解并解决这两个困扰你的技术问题,它们都是Immutable.js与Next.js SSR结合时的常见坑:
问题1:/me/profile-setting页面getServerSideProps的JSON序列化错误
问题根源
错误提示里说.initialState.ui无法被JSON序列化,这是因为你的uiReducer和meReducer都用fromJS()把初始状态转换成了Immutable.js的Map对象。而Next.js在SSR过程中,需要把Redux store的状态序列化成JSON传递给客户端,但Immutable的特殊对象结构并不属于原生JSON支持的数据类型,所以直接序列化会失败。
修复方案
最优雅的方式是在next-redux-wrapper的配置中全局处理Immutable状态的序列化/反序列化,这样所有页面的SSR都会自动适配:
修改你的modules/store.js(store的创建文件),在createWrapper时添加序列化逻辑:
import { createWrapper } from 'next-redux-wrapper'; import { fromJS, toJS } from 'immutable'; // ... 其他store相关导入和创建逻辑 const wrapper = createWrapper(makeStore, { // 把Immutable状态转成普通JS对象用于序列化 serializeState: (state) => state.toJS(), // 把客户端接收到的JS对象转回Immutable状态 deserializeState: (state) => fromJS(state), }); export { wrapper };
另外,还要修正reducer中的HYDRATE处理,确保服务器端的状态能正确合并到客户端:
- 对于
uiReducer:
case HYDRATE: // 用merge合并服务器端传来的ui状态,而不是直接返回原state return state.merge(fromJS(payload.ui));
问题2:引入Immutable.js后服务器端无法dispatch动作
问题根源
在未使用Immutable前,Redux状态是普通JS对象,next-redux-wrapper的HYDRATE逻辑可以直接合并状态,但换成Immutable后,你的reducer对HYDRATE的处理没有正确适配Immutable的结构,导致服务器端dispatch的动作状态无法传递到客户端;另外,也可能是状态更新时没有用Immutable的API导致状态没变化。
修复方案
修正reducer中的HYDRATE和状态更新逻辑:
meReducer的HYDRATE处理:如果服务器端传来的payload.me是普通JS对象(因为序列化转成了JS),要先转成Immutable:
case HYDRATE: console.log({ payload }); return state.set("currentPage", fromJS(payload.me.currentPage));- 确保所有状态更新都用Immutable的API(比如
set、merge),不要直接修改状态对象。
验证服务器端dispatch的动作是否生效:
在meReducer的SWITCH_ME_PAGEcase中加个日志,确认服务器端dispatch时是否触发了状态更新:case Actions.SWITCH_ME_PAGE: console.log('服务器端触发页面切换:', payload); return state.set("currentPage", payload);组件中获取状态时用Immutable的
get方法:
比如在组件中获取currentPage时,要这样写:const currentPage = useSelector(state => state.me.get('currentPage'));不要直接用
state.me.currentPage,因为me是Immutable对象,不是普通JS对象。
额外小提示
- 所有reducer的初始状态都要用
fromJS()包裹,保持状态结构统一; - 如果你在组件中需要把Immutable对象转成普通JS对象使用,可以用
toJS()方法,比如state.ui.toJS(),但注意不要频繁调用,避免性能问题。
内容的提问来源于stack exchange,提问作者Ameed Faridi

