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

如何在Redux Toolkit的createSlice中减少代码重复?

解决Redux Toolkit中reducer代码重复的方案

针对你遇到的重复编写set/add/remove类型reducer的问题,可以通过抽象通用reducer生成函数的方式实现逻辑复用,新增字段时只需一行代码即可生成对应的三个reducer。

步骤1:编写通用reducer生成工具函数

这个函数接收目标字段名,自动生成对应的set/addTo/removeFrom三个reducer,同时通过TypeScript泛型保证类型安全:

import { PayloadAction } from '@reduxjs/toolkit';

// 辅助函数:将字段名首字母大写,用于生成规范的reducer名称
function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * 为指定字段生成三个通用reducer:set、addTo、removeFrom
 * @param field 目标字段名(如'sliceObjectA')
 */
function createSliceObjectReducers<T extends { id: number }, K extends keyof T>(field: K) {
  return {
    // 设置指定id条目的字段完整数组
    [`set${capitalize(field as string)}`]: (state: T[], action: PayloadAction<{ id: number; [key in K]: T[K] }>) => {
      const targetRow = state.find(row => row.id === action.payload.id);
      if (targetRow) {
        targetRow[field] = action.payload[field];
      }
    },
    // 向指定id条目的字段添加单个元素
    [`addTo${capitalize(field as string)}`]: (state: T[], action: PayloadAction<{ id: number; [key in K]: T[K] extends Array<infer U> ? U : never }>) => {
      const targetRow = state.find(row => row.id === action.payload.id);
      if (targetRow) {
        (targetRow[field] as unknown as any[]).push(action.payload[field]);
      }
    },
    // 从指定id条目的字段删除单个元素(按id匹配)
    [`removeFrom${capitalize(field as string)}`]: (state: T[], action: PayloadAction<{ id: number; [key in K]: (T[K] extends Array<infer U> ? U : never) & { id: number } }>) => {
      const targetRow = state.find(row => row.id === action.payload.id);
      if (targetRow) {
        targetRow[field] = (targetRow[field] as unknown as any[]).filter(
          (elem: { id: number }) => elem.id !== action.payload[field].id
        ) as T[K];
      }
    }
  };
}

步骤2:在createSlice中复用该函数

直接通过扩展运算符将生成的reducer合并到slice的reducers对象中,新增字段时只需添加一行调用:

// 先修正你的类型定义(确保需要操作的字段是数组类型,否则push/filter会报错)
interface SomeSliceType {
  id: number;
  sliceObjectA: SliceObjectA[];
  sliceObjectB: SliceObjectB[];
  sliceObjectC: SliceObjectC[];
  sliceObjectD: unknown;
}

export const someSlice = createSlice({
  name: "someSlice",
  initialState: [] as SomeSliceType[],
  reducers: {
    addSomeSlice: (state, action: PayloadAction<SomeSliceType>) => {
      state.push(action.payload);
    },
    // 为sliceObjectA生成三个reducer
    ...createSliceObjectReducers<SomeSliceType, 'sliceObjectA'>('sliceObjectA'),
    // 为sliceObjectB生成三个reducer
    ...createSliceObjectReducers<SomeSliceType, 'sliceObjectB'>('sliceObjectB'),
    // 后续新增字段时,只需添加这一行即可
    ...createSliceObjectReducers<SomeSliceType, 'sliceObjectC'>('sliceObjectC'),
  },
});

// 导出所有action creators(包括自动生成的)
export const {
  addSomeSlice,
  setSliceObjectA,
  addToSliceObjectA,
  removeFromSliceObjectA,
  setSliceObjectB,
  addToSliceObjectB,
  removeFromSliceObjectB,
  setSliceObjectC,
  addToSliceObjectC,
  removeFromSliceObjectC
} = someSlice.actions;

方案优势

  • 彻底消除重复代码:通用逻辑只需定义一次,新增字段时无需复制粘贴重复代码。
  • 类型安全:通过TypeScript泛型约束字段名和payload类型,避免类型错误。
  • 扩展性强:后续无论新增多少个类似字段,都能通过一行代码生成对应的reducer。

内容的提问来源于stack exchange,提问作者Ericko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 00:45:57