1

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.

papillon
  • 1,807
  • 2
  • 19
  • 39
  • 1
    It's really a matter of opinion if using mutable values which you've never *seen* cause a bug is "OK" or not. – Quentin Feb 15 '21 at 13:59
  • @Quentin to me, if someone tells me that this code has a probability of causing a bug, even 1 time in a thousand, then this code is wrong (even if I have yet to see the bug happen). However if it is known that it WON'T cause any bug or any kind of wrong behaviour such as a problem of performance, then this code is fine. – papillon Feb 15 '21 at 14:04
  • I have updated my question for it to ask not for an opinion, but for some facts. – papillon Feb 15 '21 at 14:07
  • 1
    Consider that state updates are asynchronous, batched, optimized, etc... It seems like an odd question to even temp the occurrence of bugs rather than just do it right in the first place. It's a common question on Stack Overflow and generally goes: "I know X is bad practice, but I do it anyway and it seems to work. Change my mind." It's often not an answerable question as any suggestion is met with "but it works for me". If it works for you and you're okay with it, go for it. Things that "seem to work" despite anti-patterns have a tendency to work fine right up until the moment they don't. – David Feb 15 '21 at 14:30
  • 2
    here you have a better explanation about the issue of mutating directly https://stackoverflow.com/questions/37755997/why-cant-i-directly-modify-a-components-state-really. This is a well stablished approach in community for good reasons. Regardless, it's always valid to propose new approaches but with some solid arguments rather than a shallow _"well, it still works!!"_. all in all, I would not consider your colleague as pragmatic but as lazy instead. – buzatto Feb 15 '21 at 14:38

1 Answers1

1

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).

Jonathan
  • 3,614
  • 3
  • 21
  • 30
  • actually setDirectly does is not followed by a re-render, which confirms that mutating the state directly is wrong. the function that does work and should not is setDirectlyButSafely – papillon Feb 15 '21 at 14:30
  • Why should it not? You *can* mutate the state, and nobody prevents you from. The `setState` then receives the current value that the object holds and react "does its thing" – Jonathan Feb 15 '21 at 14:49
  • sry that's not what I meant: I can, indeed, use this method, but this is not the standard way of mutating the state. And I need to know if it can cause problems of any sort, or if it's fine – papillon Feb 15 '21 at 14:53