添加函数到Redux Slice初始状态属性后报错(TypeScript)
问题解决思路与代码修正
核心问题分析
- Redux State 不可存储函数:Redux要求状态必须是可序列化的纯数据(对象、数组、基本类型),函数无法被序列化,会导致DevTools异常、状态持久化失败,同时原reducer中操作函数的逻辑本身就不符合预期。
- Reducer逻辑错误:原代码中
changePlayableFields尝试解构playableFields函数,这会得到一个空对象(函数自身属性极少),自然找不到rows属性,触发报错。
方案一:用Selector动态生成playableFields(适合无需修改单个cell的场景)
如果playableFields仅需根据width和height动态生成,不需要单独修改某个cell的状态,直接用Redux Toolkit的记忆化Selector来处理:
修正后的Slice代码
import { createSlice, createSelector } from "@reduxjs/toolkit"; const levelSlice = createSlice({ name: "level", initialState: { // ...其他状态 gridSizeAndPlayableFields: { width: 8, height: 9 // 移除playableFields函数 } }, reducers: { // ...其他reducers changeGridSize: (state, action) => { state.gridSizeAndPlayableFields.width = action.payload.width; state.gridSizeAndPlayableFields.height = action.payload.height; } } }); // 创建记忆化Selector,仅当width/height变化时重新计算 export const selectPlayableFields = createSelector( [state => state.level.gridSizeAndPlayableFields], ({ width, height }) => ({ rows: [...Array(height)].map(() => ({ cells: [...Array(width)].map(() => ({ status: true })) })) }) ); export const { changeGridSize } = levelSlice.actions; export default levelSlice.reducer;
组件中使用
在组件里通过useSelector获取生成的playableFields:
import { useSelector } from "react-redux"; import { selectPlayableFields } from "./levelSlice"; // ... const playableFields = useSelector(selectPlayableFields); // ...
方案二:将playableFields作为数据存入State(需要修改单个cell的场景)
如果需要支持点击切换单个cell的status,则需要把playableFields的实际数据存在state中,初始化时直接生成数据而非函数:
修正后的Slice代码
import { createSlice } from "@reduxjs/toolkit"; // 辅助函数:生成初始playableFields数据 const generatePlayableFields = (width: number, height: number) => ({ rows: [...Array(height)].map(() => ({ cells: [...Array(width)].map(() => ({ status: true })) })) }); const levelSlice = createSlice({ name: "level", initialState: { // ...其他状态 gridSizeAndPlayableFields: { width: 8, height: 9, playableFields: generatePlayableFields(8, 9) // 初始化时生成数据 } }, reducers: { // ...其他reducers changeGridSize: (state, action) => { const { width, height } = action.payload; state.gridSizeAndPlayableFields.width = width; state.gridSizeAndPlayableFields.height = height; // 尺寸变化时重新生成playableFields state.gridSizeAndPlayableFields.playableFields = generatePlayableFields(width, height); }, changePlayableFields: (state, action) => { const { i, k } = action.payload; // Redux Toolkit内置Immer,允许直接"修改"state state.gridSizeAndPlayableFields.playableFields.rows[i].cells[k].status = !state.gridSizeAndPlayableFields.playableFields.rows[i].cells[k].status; } } }); export const { changeGridSize, changePlayableFields } = levelSlice.actions; export default levelSlice.reducer;
关键优化点
- 用辅助函数
generatePlayableFields统一生成结构,避免重复代码。 - 利用Redux Toolkit的Immer特性,无需手动解构复制嵌套对象,直接修改状态即可保证不可变性。
内容的提问来源于stack exchange,提问作者saintyusuf
相关产品推荐
相关产品推荐

