在Redux Toolkit中存储循环引用对象触发栈溢出错误如何解决?
问题根因
该报错是Redux Toolkit默认开启的可序列化状态检查导致的:检查逻辑会递归遍历完整state树校验内容是否可序列化,遇到循环引用对象时会进入无限递归,最终触发栈溢出。同时Redux内部使用的Immer库处理循环引用对象也可能产生异常。
可行方案
方案1:针对性关闭序列化检查(改动最小,优先推荐)
如果你不需要对该client对象做状态持久化、也不需要用到时间旅行调试功能,直接在configureStore配置中忽略对应路径的序列化检查即可,不需要修改现有业务逻辑:
import { configureStore } from '@reduxjs/toolkit' export const store = configureStore({ reducer: { chat: chatSlice.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { // 忽略client字段的序列化检查 ignoredPaths: ['chat.client'], // 可同步忽略对应的addClient action校验 ignoredActions: ['chatSlice/addClient'], }, }), })
配置后即可直接将循环引用对象存入store,多个组件可正常从store取到对象引用使用。
方案2:外部映射存储(符合Redux序列化规范)
如果需要保留全局序列化检查,可以将实际的client对象存在全局Map中,store仅存储对应的唯一索引:
// 全局存储client对象的映射表 const globalClientMap = new Map() // slice调整为存储索引 const chatSlice = createSlice({ name: 'chatSlice', initialState: initialState, reducers: { addClient: (state, action: PayloadAction<string>) => { state.clientId = action.payload } } }) // dispatch前先存入映射表 const clientId = 'unique_client_id' // 可使用uuid或自定义规则生成唯一标识 globalClientMap.set(clientId, conversationsClient) dispatch(addClient(clientId)) // 组件使用时先取索引再拿实际对象 const clientId = useSelector(state => state.chat.clientId) const conversationsClient = globalClientMap.get(clientId)
该方案完全符合Redux的可序列化要求,适合需要做状态持久化、时间旅行调试的场景,注意对象销毁时要同步删除Map中的对应项避免内存泄漏。
方案3:循环引用序列化存储
你之前使用flatted未生效是处理时机不对,需要在传入action前完成序列化,取值时再做反序列化:
import { stringify, parse } from 'flatted' // dispatch前序列化循环引用对象 dispatch(addClient(stringify(conversationsClient))) // slice存储序列化后的字符串 reducers: { addClient: (state, action: PayloadAction<string>) => { state.client = action.payload } } // 组件取值时反序列化 const clientStr = useSelector(state => state.chat.client) const conversationsClient = parse(clientStr)
该方案的缺点是每次反序列化都会生成新的对象引用,如果你需要跨组件保持同一个对象引用则不适用。
内容的提问来源于stack exchange,提问作者Marwan Al-Masaeid
相关产品推荐
相关产品推荐

