papillon
papillon

Reputation: 2062

Will mutating React's state directly before calling setState cause any problem?

First of all, I know there's a similar question but its accepted answer does not actually answer the question, so I am asking it again.

It is well known that we should not mutate the state directly, because of many issues that can happen. I personally always make a copy of the data I will update, mutate this copy, and assign it to the original variable in the state with a call to setState, which is the common and canonical way of updating the state:

let copy = JSON.parse(JSON.stringify(this.state.someDeepObject))
copy.a.b.c = 5
this.setState({ someDeepObject: copy }) 

However a colleague of mine uses an alternative way of updating the state. He mutates the state directly, and then uses the updated value in the state to update the state with setState, enabling a re-render and preventing all problem that a direct mutation would normally cause:

this.state.someDeepObject.a.b.c = 5
this.setState({ someDeepObject: this.state.someDeepObject })

Since he calls setState right after mutating the state, this way of mutating the state, although tricky and not quite reliable, works, and has never bugged anywhere it is used in the application, as far as I know.

It is not the canonical way of mutating the state, however, it works. I would use the canonical way instead, but my colleague is a pragmatic man and, if it works and is reliable (which his code seems to be), he won't change it.

A more complete example of the situation will show you that his code does work:

class Button extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0,
    };
  }

  updateCount() {
    this.setState((prevState, props) => {
      return { count: prevState.count + 1 }
    });
  }

  updateCountDirectly() {
    this.state.count = this.state.count + 1
  }

  updateCountDirectlyButSafely() {
    this.state.count = this.state.count + 1
    this.setState({ count: this.state.count })
  }

  render() {
    return (
      <div>
        <button onClick={() => this.updateCount()} >
          Clicked {this.state.count} times (normal)
        </button>
        <button onClick={() => this.updateCountDirectly()} >
          Clicked {this.state.count} times (directly)
        </button>
        <button onClick={() => this.updateCountDirectlyButSafely()} >
          Clicked {this.state.count} times (directly but safely)
        </button>
      </div>
    );
  }
}

React.render(<Button />, document.getElementById('app'));

(copy paste this in CodePen to test it)

Will his code cause any problem?

I won't use it myself but, if it doesn't actually cause any problem, then I won't bother him with it anymore.

Upvotes: 2

Views: 1053

Answers (1)

Jonathan
Jonathan

Reputation: 3654

Probably not... But it is not guaranteed. React could be doing internal stuff before it actually updates the state that is rendered and this could interfere.

Also, it seems to be working in your example only randomly. Because if you mutate the state without going through setState React has no way to tell that it should re-render the component. In your example the "setDirectly" method just seems to work because React re-renders the component anyhow (maybe because it does so after onClick callbacks).

Upvotes: 1

Related Questions