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

如何在数组中嵌套Redux Reducer?多层级状态管理求助

解决Redux嵌套Reducer的组合问题

我明白你现在的困扰——在Redux里处理嵌套的routines和days状态时,不知道怎么正确组合reducer来实现给指定routine添加day的功能。先来看你现有代码里的核心问题:

现有代码的问题

在routineReducer的ADD_ROUTINE case中,你把days赋值为dayReducer函数,这是错误的——reducer是用来处理状态更新的函数,而不是状态本身。我们需要的是初始的days状态(比如空数组)。

修正方案:嵌套状态的正确更新方式

我们需要让routineReducer能够定位到目标routine,然后调用dayReducer来更新该routine的days数组。以下是分步实现:

1. 定义Action类型(先明确操作类型)

const ADD_ROUTINE = 'ADD_ROUTINE';
const ADD_ROUTINE_DAY = 'ADD_ROUTINE_DAY';

2. 完善dayReducer

保持它的职责单一,只处理days数组的更新:

function dayReducer(state = [], action) {
  switch (action.type) {
    case ADD_ROUTINE_DAY:
      return [
        ...state,
        { 
          dayName: action.dayName,
          exercises: [] // 和你的示例状态保持一致,初始化为空练习数组
        }
      ];
    default:
      return state;
  }
}

3. 修正routineReducer

现在让它同时处理添加routine和给指定routine添加day的逻辑:

function routineReducer(state = [], action) {
  switch (action.type) {
    case ADD_ROUTINE:
      return [
        ...state,
        {
          id: Date.now().toString(), // 生成唯一ID(推荐用uuid库更可靠)
          name: action.name,
          days: [] // 初始化为空数组,而不是reducer函数
        }
      ];
    case ADD_ROUTINE_DAY:
      // 遍历找到目标routine,更新它的days数组
      return state.map(routine => {
        if (routine.id === action.routineId) {
          // 调用dayReducer更新当前routine的days状态
          return {
            ...routine,
            days: dayReducer(routine.days, action)
          };
        }
        return routine;
      });
    default:
      return state;
  }
}

4. 保持Reducer组合方式不变

const fitnessApp = combineReducers({
  routines: routineReducer,
  routing
});

5. 创建Action Creators(方便触发操作)

// 添加新routine
export const addRoutine = (name) => ({
  type: ADD_ROUTINE,
  name
});

// 给指定routine添加day
export const addRoutineDay = (routineId, dayName) => ({
  type: ADD_ROUTINE_DAY,
  routineId,
  dayName
});

优化建议:用Immer简化不可变更新

如果觉得手动处理不可变状态(展开运算符、map)太繁琐,可以用immer库来简化代码,它允许你直接“修改”状态,内部会帮你生成不可变的新状态:

import produce from 'immer';
import { v4 as uuidv4 } from 'uuid'; // 推荐用uuid生成唯一ID

function routineReducer(state = [], action) {
  return produce(state, draft => {
    switch (action.type) {
      case ADD_ROUTINE:
        draft.push({
          id: uuidv4(),
          name: action.name,
          days: []
        });
        break;
      case ADD_ROUTINE_DAY:
        const targetRoutine = draft.find(r => r.id === action.routineId);
        if (targetRoutine) {
          // 直接调用dayReducer更新days,或者用immer直接push
          targetRoutine.days = dayReducer(targetRoutine.days, action);
        }
        break;
    }
  });
}

关键要点总结

  • 不要把Reducer作为状态值:初始状态应该是对应的数据结构(比如空数组),Reducer是用来处理状态更新的函数。
  • 保持不可变性:Redux要求状态是不可变的,所以更新嵌套状态时要通过map、展开运算符或者Immer来创建新的数组/对象。
  • 用唯一ID标识列表项:避免依赖数组索引来定位routine,因为数组顺序变化会导致索引失效,用唯一ID更可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:15:16