如何在不明确提供大量操作上下文的情况下更改深层嵌套状态?

How to change deeply nested states without explicitly providing a lot of context in actions?

我有几层嵌套的项目:

const initialState = {
  sections: [
    {
      name: 'Section 1',
      categories: [
        {
          name: 'Category 1',
          checklist: [
            { name: 'Item 1', checked: false },
            { name: 'Item 2', checked: true },
            { name: 'Item 3', checked: false },
          ]
        },
        // more categories
      ],
    },
    // more sections
  ],
};

我想知道是否有一种方法可以在不显式传递大量上下文的情况下更改它们的状态(例如,切换 checked 属性):

function toggleCheckbox(sectionId, categoryId, itemId) {
  return {
    type: 'TOGGLE_CHECKBOX',
    sectionId: sectionId,
    categoryId: categoryId,
    itemId: itemId,
  };
}

function reducer(state = initialState, action) {    
  switch (action.type) {
    case 'TOGGLE_CHECKBOX':
      return Object.assign({}, state, {
        sections: state.sections.map(function (section, i) {
          return i === action.sectionId ? Object.assign({}, section, {
            categories: section.categories.map(function (category, j) {
              return j === action.categoryId ? Object.assign({}, category, {
                checklist: category.checklist.map(function (item, k) {
                  return k === action.itemId ? Object.assign({}, item, {
                    checked: !item.checked,
                  }) : item;
                }),
              }) : category;
            }),
          }) : section;
        }),
      });
    default:
      return state;
  }
}

随着部分、类别和清单的逻辑增长,我可能想将它们提取为单独的缩减器,但因为它们是嵌套的,所以我看不到这样做的方法。

这里是一个 bin 使用 React 可视化的商店。

我想我的问题有两部分:

  1. 如何将它们提取为单独的 reducer?
  2. 我如何才能避免显式提供如此多的上下文并避免所有实际上只更改嵌套项状态的映射?

嵌套对象肯定会很痛苦。 Dan 在 Redux docs 中对嵌套实体做了一些介绍。

If you have nested entities, or if you let users edit received entities, you should keep them separately in the state as if it was a database. In pagination information, you would only refer to them by their IDs. This lets you always keep them up to date.

我做了一个 jsbin 修改你的例子来演示,但它的要点包括稍微展平你的状态树。

const initialState = {
  sections: [
    {
      name: 'Section 1',
      categories: [ 'Category 1'],
    },
    // more sections
  ],
  categories: [
    {
      name: 'Category 1',
      checklist: ['Item 1', 'Item 2', 'Item 3']
    },
  ],
  items: [
    {
      name: 'Item 1',
      checked: false
    },
    {
      name: 'Item 2',
      checked: true
    },
    {
      name: 'Item 3',
      checked: false
    },
  ]

};

item reducer 现在只需要一个 name 来知道要更新哪个,并且 sections/categories 跟踪他们拥有哪些项目。现在您可以拆分专门的类别、部分和项目缩减器,每个缩减器都可以处理自己关注的领域!

如果您想要在不同类别中具有相同名称的项目,您将必须引入一个新的 ID 以将它们彼此分开...但是现在使用名称是最简单的