82 lines
3.4 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
React works in two phases: render and commit.
* The render phase determines what changes need to be made to e.g. the DOM. During this phase, React calls render and then compares the result to the previous render.
* The commit phase is when React applies any changes. (In the case of React DOM, this is when React inserts, updates, and removes DOM nodes.) React also calls lifecycles like `componentDidMount` and `componentDidUpdate` during this phase.
Render phase lifecycles include among others, the following lifecycle methods:
* `componentWillMount` (or its alias `UNSAFE_componentWillMount`)
* `componentWillReceiveProps` (or its alias `UNSAFE_componentWillReceiveProps`)
* `componentWillUpdate` (or its alias `UNSAFE_componentWillUpdate`)
These are considered unsafe and also happen to be the lifecycles that cause the most confusion within the React community and tend to encourage unsafe coding practices.
[source,javascript]
----
class Foo extends React.Component {
UNSAFE_componentWillMount() {} // Noncompliant
UNSAFE_componentWillReceiveProps() {} // Noncompliant
UNSAFE_componentWillUpdate() {} // Noncompliant
}
----
== How to fix it
Instead of `componentWillUpdate`, use `getSnapshotBeforeUpdate` together with `componentDidUpdate`. The `getSnapshotBeforeUpdate` lifecycle is called right before mutations are made. The return value for this lifecycle will be passed as the third parameter to `componentDidUpdate`.
Instead of `componentWillReceiveProps`, Use `getDerivedStateFromProps` together with `componentDidUpdate`. The `getDerivedStateFromProps` lifecycle is invoked after a component is instantiated as well as before it is re-rendered. It can return an object to update state, or null to indicate that the new props do not require any state updates.
As for `componentWillMount`, React will call it immediately after the constructor. It only exists for historical reasons and should not be used. Instead, use one of the alternatives:
* To initialize state, declare `state` as a class field or set `this.state` inside the `constructor`.
* If you need to run a side effect or set up a subscription, move that logic to `componentDidMount` instead.
=== Code examples
==== Noncompliant code example
[source,javascript,diff-id=2,diff-type=noncompliant]
----
class myComponent extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() { // Noncompliant: "componentWillMount" is deprecated
if (localStorage.getItem("token")) {
this.setState({logged_in: true});
}
}
// ...
}
----
==== Compliant solution
[source,javascript,diff-id=2,diff-type=compliant]
----
class myComponent extends React.Component {
constructor(props) {
super(props);
if (localStorage.getItem("token")) {
this.setState({logged_in: true});
}
}
// ...
}
----
== Resources
=== Documentation
* React Documentation - https://react.dev/reference/react/Component#unsafe_componentwillmount[`UNSAFE_componentWillMount`]
* React Documentation - https://react.dev/reference/react/Component#unsafe_componentwillreceiveprops[`UNSAFE_componentWillReceiveProps`]
* React Documentation - https://react.dev/reference/react/Component#unsafe_componentwillupdate[`UNSAFE_componentWillUpdate`]
* React Documentation - https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#migrating-from-legacy-lifecycles[Migrating from Legacy Lifecycles]