You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加函数到Redux Slice初始状态属性后报错(TypeScript)

问题解决思路与代码修正

核心问题分析

  1. Redux State 不可存储函数:Redux要求状态必须是可序列化的纯数据(对象、数组、基本类型),函数无法被序列化,会导致DevTools异常、状态持久化失败,同时原reducer中操作函数的逻辑本身就不符合预期。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 09:01:31