Redux Reducer - How to update an object value without mutating the state?

Drew picture Drew · Aug 18, 2016 · Viewed 7.5k times · Source

My reducer looks like this:

  const players = (state = {}, action) => {
    switch (action.type) {
      case 'UPDATE_PLAYERS_CARDS':
        return Object.assign({}, state, {
          [action.player]: {
            name: state[action.player].name,
            score: state[action.player].score,
            cards: action.cards
          }
        })
       default:
         return state
    }
  }

  export default players

Basically, I just want to update the players cards but if I don't include the name and the score, then those get deleted in the new state.

Ideally I need to just do something like this: state[action.player].cards = action.cards but one of the rules is to never mutate the state.

The method above will work, but that requires me to always know the data structure so if I add something else to it in the future, I would need to remember to come back to this reducer and add that back in so I don't lose it.

Is there a better way to accomplish this?

Edit: Thanks to Vijay for the suggestion for multiple object assigns. I ended up getting it to work with this:

case 'UPDATE_PLAYERS_CARDS':
  const playerObject = Object.assign(state[action.player], { cards: action.cards });
  return Object.assign({}, state, {
    [action.player]: playerObject
  });

How is that? Let me know if there are any improvements I can make.

Answer

Ashish Chaudhary picture Ashish Chaudhary · Aug 18, 2016

Multiple Object.assign methods look ugly.

You can do:

return { ...state.action.player, cards: action.cards }

Note: You'll need stage-2 babel preset to use this.

Once the data structure to update becomes fairly complex, you can use ImmutableJS 's setIn, updateIn methods to do deep updates easily.