即使不改变对象和数组,也无法更改减速器中的嵌套对象

IT技术 reactjs ecmascript-6 redux react-redux
2021-05-17 19:56:39

我正在尝试更新减速器内的状态,我知道我不应该改变对象或嵌套对象,所以我使用map数组或对象传播对象。但似乎我无法真正更改深度嵌套的值。

除了我无法更改状态这一事实之外,我真的不喜欢代码的外观,尤其是我只更改一个属性所需执行的循环次数。我觉得有一种更好、可读性更高且性能更高的方法来做到这一点。

这是状态:

const items = [{
  name: 'item 1',
  id: 'item1',
  tags: [{
    id: 'tag1',
    name: 'tag 1'
  }, {
    id: 'tag2',
    name: 'tag 2'
  }]
}, {
  name: 'item 2',
  id: 'item2',
  tags: [{
    id: 'tag1',
    name: 'tag 1'
  }, {
    id: 'tag4',
    name: 'tag 4'
  }]
}];

这是我正在派遣的行动:

const action = {
  type: 'CHANGE_TAG_NAME',
  payload: {
    itemId: 'item2',
    tagId: 'tag4',
    newTagName: 'tag 44444'
  }
};

这是减速器:

const itemsReducer = (state = [], action) => {
  switch (action.type) {
    case 'CHANGE_TAG_NAME':
      {
        const itemIndex = state.findIndex(item => item.id === action.payload.itemId);
        const tagIndex = state[itemIndex].tags.findIndex(t => t.id === action.payload.tagId);
        const nextTag = {
          ...state[itemIndex].tags[tagIndex],
            name: action.payload.newTagName
        };
        const nextTags = [
          ...state[itemIndex].tags.slice(0, tagIndex),
          nextTag,
          ...state[itemIndex].tags.slice(tagIndex + 1, ),
        ];
        const nextItem = {
          ...state[itemIndex],
            tags: nextTags
        };

        const nextState = [
          ...state.slice(0, itemIndex),
          nextItem,
          ...state.slice(itemIndex + 1)
        ];
      }
    default:
      return state;
  }
};
2个回答

你的减速器应该工作得很好,你只是忘记nextState在你的案例块中返回

至于更少的迭代,我建议使用这种模式:
映射项目,如果当前项目的 ID 与itemId您在有效负载中的 ID 不同,则按原样返回。
如果项目的 id 相同,则返回一个新对象,然后映射标签,执行与项目相同的条件。
如果标签的 idtagId与有效负载中的 id 不同,则按原样返回,如果相同则返回一个新对象。

这是一个运行示例:

const items = [{
  name: 'item 1',
  id: 'item1',
  tags: [{
    id: 'tag1',
    name: 'tag 1'
  }, {
    id: 'tag2',
    name: 'tag 2'
  }]
}, {
  name: 'item 2',
  id: 'item2',
  tags: [{
    id: 'tag1',
    name: 'tag 1'
  }, {
    id: 'tag4',
    name: 'tag 4'
  }]
}];

const action = {
  type: 'CHANGE_TAG_NAME',
  payload: {
    itemId: 'item2',
    tagId: 'tag4',
    newTagName: 'tag 44444'
  }
};

const itemsReducer = (state = [], action) => {
  switch (action.type) {
    case 'CHANGE_TAG_NAME':
      {
        const {
          payload: {
            itemId,
            tagId,
            newTagName
          }
        } = action;
        const nextState = state.map(item => {
          if (item.id !== itemId) return item;
          return {
            ...item,
            tags: item.tags.map(tag => {
              if (tag.id !== tagId) return tag;
              return {
                ...tag,
                name: newTagName
              }
            })
          }
        });
        return nextState;
      }
    default:
      return state;
  }
};

console.log(itemsReducer(items, action));

至于更易读的代码,我建议使用更多的reducer。
我使用的一个经验法则是为每个实体创建一个 reducer:
itemsReducer、
itemReducer、
tagsReducer、
tagReducer。

这样每个减速器将负责自己的数据。

这是一个运行示例:

const items = [{
  name: 'item 1',
  id: 'item1',
  tags: [{
    id: 'tag1',
    name: 'tag 1'
  }, {
    id: 'tag2',
    name: 'tag 2'
  }]
}, {
  name: 'item 2',
  id: 'item2',
  tags: [{
    id: 'tag1',
    name: 'tag 1'
  }, {
    id: 'tag4',
    name: 'tag 4'
  }]
}];

const action = {
  type: 'CHANGE_TAG_NAME',
  payload: {
    itemId: 'item2',
    tagId: 'tag4',
    newTagName: 'tag 44444'
  }
};

const tagReducer = (state = {}, action) => {
  switch (action.type) {
    case 'CHANGE_TAG_NAME':
      {
        const {
          payload: {
            newTagName
          }
        } = action;
        const nextState = {
          ...state,
          name: newTagName
        }
        return nextState;
      }
    default:
      return state;
  }
}

const tagsReducer = (state = [], action) => {
  switch (action.type) {
    case 'CHANGE_TAG_NAME':
      {
        const {
          payload: {
            tagId
          }
        } = action;
        const nextState = state.map(tag => {
          if (tag.id !== tagId) return tag;
          return tagReducer(tag, action);
        });
        return nextState;
      }
    default:
      return state;
  }
}


const itemReducer = (state = {}, action) => {
  switch (action.type) {
    case 'CHANGE_TAG_NAME':
      {
        const nextState = {
          ...state,
          tags: tagsReducer(state.tags, action)
        }
        return nextState;
      }
    default:
      return state;
  }
}

const itemsReducer = (state = [], action) => {
  switch (action.type) {
    case 'CHANGE_TAG_NAME':
      {
        const {
          payload: {
            itemId
          }
        } = action;
        const nextState = state.map(item => {
          if (item.id !== itemId) return item;
          return itemReducer(item, action)
        });
        return nextState;
      }
    default:
      return state;
  }
};

console.log(itemsReducer(items, action));

这种模式通常称为减速器组合,您不必将它们全部包含在根减速器中,只需将它们用作外部纯函数,为其他减速器计算状态的相关部分。

你忘记了关键词 return

    //.....
    const nextState = [
      ...state.slice(0, itemIndex),
      nextItem,
      ...state.slice(itemIndex + 1)
    ];
    // HERE RETURN 🎈
    return newState;
  }
default:
  return state;