github-actions[bot] a9ed8edc19
Create rule S6746: In React this.state should not be mutated directly (#3009)
https://github.com/SonarSource/SonarJS/issues/4128
---------

Co-authored-by: alexander-kamushkin-sonarsource <alexander-kamushkin-sonarsource@users.noreply.github.com>
2023-09-06 09:05:32 +02:00

74 lines
2.5 KiB
Plaintext

== Why is this an issue?
React does not always propagate component state changes to the underlying DOM elements immediately. If you modify the state during an already ongoing update cycle, the change may be delayed until the next update. React tries to keep `this.state` in sync with what is currently in the DOM, so you should never directly modify `this.state` yourself. Instead, use the asynchronous `setState` method instead, which allows React to properly manage the current state, trigger the new update cycle, or batch the updates together if necessary.
Note that `setState()` is a _request_ to change the state, not an immediate update. For example, if multiple components are changing the state in response to a user event, React will wait until the event handler has finished executing and then render all the changes in a single update. In other cases, the updates may be executed one at a time, so you should never make assumptions about how the component state will change in response to your request.
If your next state is a function of the current state, you should pass an updater function to the `setState()` that will give you access to the correct component state at the time of the execution.
The only place where you should directly modify the state is during the component initialization in a constructor function.
[source,javascript,diff-id=1,diff-type=noncompliant]
----
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
count: 0
};
}
incrementCount() {
this.state.count++; // Noncompliant: direct mutation of state object
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.incrementCount}>Increment</button>
</div>
);
}
}
----
To fix this code use `setState()` method instead of directly mutating the state.
[source,javascript,diff-id=1,diff-type=compliant]
----
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
count: 0
};
}
incrementCount() {
this.setState(prevState => ({
count: prevState.count + 1
}));
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.incrementCount}>Increment</button>
</div>
);
}
}
----
== Resources
=== Documentation
* https://react.dev/reference/react/Component#setstate[React - ``setState()`` method]
* https://react.dev/learn/managing-state[React - Managing state]