即使不改变对象和数组,也无法在reducer中更改嵌套对象

时间:2017-12-02 19:27:38

标签: reactjs ecmascript-6 redux react-redux

我正在尝试更新reducer中的状态,我知道我不应该改变对象或嵌套对象,所以我使用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 个答案:

答案 0 :(得分:2)

您忘记了关键字return

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

答案 1 :(得分:2)

你的减速机应该工作得很好,你只是忘了在你的案件块中返回nextState

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

这是一个正在运行的例子:



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));




至于更易读的代码,我建议使用更多的减速器 我使用的规则的拇指是为每个实体创建一个减速器:
itemsReducer,
itemReducer,
tagsReducer,
tagReducer。

这样每个reducer都会负责自己的数据。

这是一个正在运行的例子:



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));




此模式通常称为reducer composition,您不必将它们全部包含在根reducer中,只需将它们用作外部纯函数,它将计算其他reducer的相关状态部分。