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

嵌套Redux Reducer执行ADD_TODO后状态返回null问题求助

解决嵌套Redux Reducer + Reselect下ADD_TODO后的异常状态问题

我帮你梳理下问题所在,大概率是你的嵌套Reducer职责划分和状态更新逻辑没对齐,才导致ADD_TODO触发后出现了{0: null, 1: null}这种异常状态。咱们一步步拆解修复:

1. 明确Reducer的职责划分

你现在的代码里有一个todo reducer,但看起来它的职责和todos列表的管理混在一起了。正确的嵌套结构应该是:

  • 单个todo的reducer:只负责处理单个todo对象的状态变化(比如创建、切换完成状态)
  • todos列表的reducer:负责管理整个todo数组,调用单个todo reducer来生成/更新列表项

修正单个todo的Reducer

// 单个todo的reducer,仅处理单个todo的状态
const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      // 补全你截断的completed字段,生成标准todo对象
      return { 
        id: action.id, 
        text: action.text, 
        completed: false 
      };
    // 可选:添加其他todo相关的action处理,比如切换完成状态
    case 'TOGGLE_TODO':
      if (state.id !== action.id) return state;
      return { ...state, completed: !state.completed };
    default:
      return state;
  }
};

修正todos列表的Reducer

这个reducer负责维护todo数组,调用上面的单个todo reducer来处理新增/更新:

// 管理todo列表的reducer
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      // 调用单个todo reducer生成新todo,再添加到数组末尾
      return [...state, todo(undefined, action)];
    case 'TOGGLE_TODO':
      // 遍历数组,对匹配的todo执行状态更新
      return state.map(t => todo(t, action));
    default:
      return state;
  }
};

组合根Reducer

把todos和visibilityFilter组合成根reducer,确保状态结构和初始状态匹配:

import { combineReducers } from 'redux';
import { fromJS } from 'immutable';

// 单独处理visibilityFilter的reducer
const visibilityFilter = (state = 'SHOW_ALL', action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter;
    default:
      return state;
  }
};

// 组合所有子reducer
const rootReducer = combineReducers({
  todos,
  visibilityFilter
});

// 注意:如果坚持用Immutable.js的fromJS初始化状态,
// 要确保todos reducer也处理Immutable数组(比如用state.push()而非展开运算符),
// 更推荐用普通对象配合展开运算符/immer,降低复杂度

2. 修正Reselect选择器的逻辑

你的makeSelectTodos需要正确匹配最终的状态结构。如果根状态直接包含todos字段,选择器应该这么写:

// containers/HomePage/selectors.js
import { createSelector } from 'reselect';

// 先从根状态提取todos
const selectTodos = state => state.todos;

// 创建记忆化选择器
const makeSelectTodos = createSelector(
  selectTodos,
  // 如果是普通数组直接返回;如果是Immutable数组,按需用toJS()转换成普通对象
  todos => todos
);

export { makeSelectTodos };

3. 为什么会出现{0: null, 1: null}?

这种异常状态大概率是因为:

  • 你错误地把单个todo reducer直接当成了todos列表的reducer,导致ADD_TODO时返回了单个对象,Redux把这个对象当成了数组的“索引键值对”,未被覆盖的索引就变成了null
  • 或者你使用Immutable.js时,没有正确处理数组的更新操作,导致Immutable数组被意外转换成了普通对象

按照上面的步骤调整后,ADD_TODO触发后状态应该会变成{ todos: [{id:1, text:'xxx', completed:false}, ...], visibilityFilter: 'SHOW_ALL' }的正常结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:00:32