Skip to content

Question: Mutation of an inner object in a state in a reducer #1623

@elado

Description

@elado

Is this a valid redux reducer?

function reducer(state={ byId: {}, ids: [] }, action) {
  switch (action.type) {
    case 'ADD':
      state = { ...state }
      state.byId[action.id] = action.value
      state.ids.push(action.id)
      return state

    default:
      return state
  }
}

The entire state is shallow cloned first, so a new object will be returned, but the inner objects stay the same and mutated.

Or do I have to do something like:

function reducer(state={ byId: {}, ids: [] }, action) {
  switch (action.type) {
    case 'ADD':
      return {
        ...state,
        byId: {
          ...state.byId,
          [action.id]: action.value
        },
        ids: [ ...state.ids, action.id ]
      }

    default:
      return state
  }
}

The side effects I see with the first approach are in DevTools LogMonitor:

  • expanding a sub node (e.g. byId) in a previous state will show the same value as the latest one even when it was something else previously
  • time travel isn't working

Is it just a bug in the LogMonitor or the second approach is the right one? If I add state = _.cloneDeep(state) before the mutations it actually works fine, proving that the LogMonitor reuses the same objects, hence the same values.

The rest of the app works fine and @connected views update correctly though.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions