tdammon
tdammon

Reputation: 589

React Redux, Am I mutating state here?

I am playing around with a simple React Redux app that increments a counter each time a button is clicked. I am testing two different methods of incrementing my counter in my Redux Reducer. Both methods are changing the state in the reducer however one of these methods is not causing my counter to rerender.

Here is the reducer which will cause the page to rerender:

const reducer = (state={speed: 0}, action) => {

  if(action.type ==='INCREMENT_SPEED'){
    state = {...state, speed: state.speed+=1 
    }
    return state 
}

and here is the code that functions but will not cause the page to rerender with the proper value.

const reducer = (state={speed: 0}, action) => {

      if(action.type ==='INCREMENT_SPEED'){
        state.speed++ 
    }
    return state 
}

The only difference is the way in which I am updating state. My guess is that using the ++ incrementer is actually mutating state which is not seen as a change and hence does not cause the page to render.

If it helps here is the part of the code that appends to the DOM:

render() {
    return (
      <div>

        <p>SPEED: {this.props.reduxState.reducer.speed}</p>

      </div>
    )
  }
}

Upvotes: 2

Views: 2958

Answers (3)

Joaquin Timerman
Joaquin Timerman

Reputation: 224

If you checkout Redux doc you will find that they suggest using either Object.assign() or Object spread syntax to prevent mutating the state.

Both solutions are viable.

On a side note, the use of switch statements for reducers is encouraged.

Using Object.assign()

const reducer = (state={speed: 0}, action) => {
  switch (action.type) {
    case 'INCREMENT_SPEED':
      return Object.assign({}, state, {
        speed: state.speed + 1
      })
    default:
      return state
  }
}

Using Object spread syntax

const reducer = (state={speed: 0}, action) => {
  switch (action.type) {
    case 'INCREMENT_SPEED':
      return {...state, speed: state.speed + 1}
    default:
      return state
  }
}

Upvotes: 4

weibenfalk
weibenfalk

Reputation: 892

You're correct in your assumption. In the second example you're mutating the state and that will not trigger a re-render. You should never mutate the state. Always make a copy of the state and return a new state.

Upvotes: 5

Robert
Robert

Reputation: 20286

Yes you are mutating the original state instead of returning new state object with incremented counter.

You could do like that(not much sense in this case but shows the problem)

const reducer = (state={speed: 0}, action) => {

  if (action.type ==='INCREMENT_SPEED'){
    state = {...state} // copy the state to a new object
    state.speed++; // increment the new object

    return state; // return the new state object 
}

That way it's a new object so the old state objects remains immutable

Upvotes: 4

Related Questions