改变减速器上的状态对象

Changing state object on reducer

我正在尝试更改我的减速器上的一个对象,正如我在其他地方读到的那样,Object.assign 在该对象上创建了一个新副本,但元素是相同的 (),所以我不断收到

Invariant violation, state mutation was detected inside dispatch

尝试时

let newTravelersObject = Object.assign({}, state.travelers);

我想如果我有一个数组,我可以使用 map,我也尝试过使用 loadhash _.mapValues 但得到同样的错误。

另一种解决方案是重组我的代码以使用数组而不是对象,但我认为我不应该这样做,我拒绝相信它无法完成

state.travelers 对象

{
  "results": [
    {"id" : 34,
      "name": "",
      "enemies" : [
        {"profession": "Dentist", "id": 54},
        {"profession": "Police officer", "id": 471}
    },  
    {"id" : 52,
      "name": "Alicia",
      "enemies" : [
        {"profession": "Architect", "id": 61}
    },
    ...
  ],
  "count": 7
}



case types.ADD_ENEMY_SUCCESS:

  let newTraverlersObject = Object.assign({}, state.travelers);     

  let id = _.findIndex(newTraverlersObject.results, ["id", action.enemy.traveler_id]);
  newTraverlersObject.results[id].enemies.push({"id": action.enemy.id, "profession": action.enemy.profession});


  return Object.assign({}, state, {
    travelers: newTravelersObject
  });

class 的构造函数,其中状态为

class TravelersPage extends React.Component {
  constructor(props, context){
    super(props, context);
    this.state = {
      travelers: Object.assign({}, props.travelers),
      currentTravelerId: null,
      loading: true,
      ...
    };

您正在改变嵌套对象,例如 results[id].enemies.push
请注意 .findIndex 将 return 是浅拷贝而不是深拷贝。

我建议重新考虑这里的模式并考虑更简洁和可读的流程。

只是 return 一个新对象,但映射到结果数组和 return 不具有相同 traveler_id 的旅行者,当你找到相关的 traveler return一个新对象并覆盖它的enemies数组,return一个具有ES2015传播特性的新数组并在最后添加新的敌人对象。

这是一个 运行 示例:

const newItems = {
  "results": [{
    "id": 34,
    "name": "",
    "enemies": [{
      "profession": "Dentist",
      "id": 54
    }, {
      "profession": "Police officer",
      "id": 471
    }]
  }, {
    "id": 52,
    "name": "Alicia",
    "enemies": [{
      "profession": "Architect",
      "id": 61
    }],
  }]
};
const action = {
  type: 'ADD_ENEMY_SUCCESS',
  enemy: {
    id: 999,
    traveler_id: 34,
    profession: 'my new proffesion!!!'
  }
};
const travelersReducer = (state = {}, action) => {
  switch (action.type) {
    case 'ADD_ENEMY_SUCCESS':
      {
        const {
          enemy: {
            id,
            traveler_id,
            profession
          }
        } = action;
        const nextState = {
          ...state,
          results: state.results.map(traveler => {
            if (traveler.id !== traveler_id) return traveler; // this is not out target, return the object as is
            return { // return a new object
              ...traveler,
              enemies: [
                ...traveler.enemies, { // new enemy
                  id,
                  profession,
                }
              ]
            }
          })
        };
        return nextState;
      }
      defualt: return state;
  }
}

console.log(travelersReducer(newItems, action));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

我认为你应该添加更多的 reducer,我的经验是为每个实体创建一个 reducer(travelersReducer、travelerReducer、enemiesReducer、enemyReducer...)。

我实际上 不久前还包含了一个创建更多 reducer 的示例。

请注意,我没有使用 Object.assign,而是使用了 object spread feature which is a proposal but in stage 3