Life cycle overview
projects.wojtekmaj.pl/react-lifec…
The life cycle of the yellow box is React17.0 Removed lifecycle functions
Life cycle description
/* eslint-disable no-script-url */
/* eslint-disable jsx-a11y/anchor-is-valid */
import React, { Component } from "react";
class LifeCycle extends React.Component {
constructor(props) {
super(props);
this.state = {
num: Math.random() * 100,
};
this.childRef = React.createRef();
console.log('parent constructor')
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("parent getDerivedStateFromProps");
if (nextProps.isUpdate) {
return { str: "getDerivedStateFromProps update state" };
}
return null;
}
// componentWillReceiveProps(nextProps, prevState) {
// debugger
// console.log("componentWillReceiveProps()");
// }
componentDidMount() {
console.log("parent componentDidMount");
// this.setState({
// str: "str",
// });
}
shouldComponentUpdate(nextProps, nextState) {
console.log("parent shouldComponentUpdate");
return true; // Remember to return true
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("parent getSnapshotBeforeUpdate");
return {
name: "componentWillUpdate",
};
}
// componentWillUpdate(prevProps, prevState) {
// console.log("componentWillUpdate");
// return {
// name: 'componentWillUpdate'
// }
// }
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("parent componentDidUpdate");
}
componentWillUnmount() {
console.log("parent componentWillUnmount");
}
propsChange() {
console.info(" Update parent component state");
this.setState({
num: Math.random() * 100,
});
}
setLifeCycleState() {
console.info(" Update subcomponents state");
this.childRef.current.setTheState();
}
forceLifeCycleUpdate() {
console.info(" Force update of subcomponents ");
this.childRef.current.forceItUpdate();
}
parentForceUpdate() {
console.info(" Force update of parent component ");
this.forceUpdate();
}
render() {
console.log("parent render")
return (
<div> <button className="weui_btn weui_btn_primary" onClick={this.propsChange.bind(this)} > Update parent component state </button> <button className="weui_btn weui_btn_primary" onClick={this.setLifeCycleState.bind(this)} > Update subcomponents state </button> <button className="weui_btn weui_btn_primary" onClick={this.forceLifeCycleUpdate.bind(this)} > forceUpdate Child components </button> <button className="weui_btn weui_btn_primary" onClick={this.parentForceUpdate.bind(this)} > forceUpdate Parent component </button> <Message ref={this.childRef} num={this.state.num}></Message> </div>
);
}
}
class Message extends Component {
constructor(props) {
super(props);
console.log("child constructor");
this.state = { str: "hello", name: "rodchen" };
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("child getDerivedStateFromProps");
if (nextProps.isUpdate) {
return { str: "getDerivedStateFromProps update state" };
}
return null;
}
// componentWillReceiveProps(nextProps, prevState) {
// debugger
// console.log("componentWillReceiveProps()");
// }
componentDidMount() {
console.log("child componentDidMount");
// this.setState({
// str: "str",
// });
}
shouldComponentUpdate(nextProps, nextState) {
console.log("child shouldComponentUpdate");
return true; // Remember to return true
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("child getSnapshotBeforeUpdate");
return {
name: "componentWillUpdate",
};
}
// componentWillUpdate(prevProps, prevState) {
// console.log("componentWillUpdate");
// return {
// name: 'componentWillUpdate'
// }
// }
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("child componentDidUpdate");
}
componentWillUnmount() {
console.log("child componentWillUnmount");
}
setTheState() {
let s = "hello";
if (this.state.str === s) {
s = "HELLO";
}
this.setState({
str: s,
});
}
forceItUpdate() {
this.forceUpdate();
}
render() {
console.log("child render");
return (
<div> <span> Props:<h2>{this.props.num}</h2> </span> <span> State:<h2>{this.state.str}</h2> </span> </div>
);
}
}
export default LifeCycle;
Copy code
monting Stage
Contructor
import React from "react";
class LifeCycle extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'rodchen'
}
}
render() {
return (
<div> <h2>Life Cycle</h2> <div> </div> </div>
);
}
}
export default LifeCycle;
Copy code
If not initialized state Or no method binding , It doesn't need to be React Component implementation constructor .
stay React Before mounting the components , Will call its constructor . For React.Component When a subclass implements a constructor , It should be called before other statements super(props). otherwise ,this.props Undefined... May appear in the constructor bug.
Usually , stay React in , Constructors are only used in the following two cases :
- By giving this.state Assign an object to initialize the interior state.
- Binding instances for event handlers
stay constructor() Do not call setState() Method . If your component needs to use internal state, Please directly in the constructor for this.state Assignment initial state:
Avoid props Copy the value of to state
constructor(props) {
super(props);
// Don't do this
this.state = { color: props.color };
}
Copy code
When props The update will not be updated to state. Only when you deliberately ignore prop Use in case of update . here , Should be prop Rename it to initialColor or defaultColor. When necessary, , You can modify its key, To force “ Reset ” Its internal state.
getDerivedStateFromProps
getDerivedStateFromProps Will be in the call render Call before method , And it will be called during initial mount and subsequent update . It should return an object to update state, If you return null Then nothing will be updated .
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps");
return {str: "getDerivedStateFromProps update state"};
}
Copy code
This method does not have access to the component instance . If you need , You can extract components by props Pure functions of and class Out of state , stay getDerivedStateFromProps() And others class Reuse code between methods . Use here this yes undefined.
Please note that , Whatever the reason , This method is triggered before each rendering . This is related to UNSAFE_componentWillReceiveProps Contrast formation , The latter is triggered only when the parent component is re rendered , Instead of calling... Internally setState when .
getDerivedStateFromProps
UNSAFE_componentWillReceiveProps
componentWillReceiveProps You can access the component instance ,this Is the current component
Because for whatever reason , This method is called every time you render , So default back to null. If props Incoming content doesn't need to affect your state, Then you need to return a null, This return value is required , So try to write it to the end of the function
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps");
if (nextProps.isUpdate) {
return {str: "getDerivedStateFromProps update state"};
}
return null;
}
Copy code
render() {
// console.log("render");
return (
<div> <span>Props:<h2>{this.props.num}</h2></span> <br/> <span>State:<h2>{this.state.str}</h2></span> </div>
);
}
Copy code
render
render() The method is class The only method that must be implemented in a component .
When render When called , It will check. this.props and this.state And return one of the following types :
- React Elements . Usually by JSX establish . for example ,
Will be React Render as DOM node , Will be React Render as a custom component , Whether it'sstill Are all React Elements .
- An array or fragments. bring render Method can return multiple elements . For more details , see also fragments file .
- Portals. You can render children to different DOM In the subtree . For more details , Please refer to the portals Documents
- String or numeric type . They are DOM Is rendered as a text node in
- Boolean type or null. Nothing to render .( Mainly used to support return test && The pattern of , among test Is Boolean type .)
render() Function should be pure function
This means that components are not modified state Under the circumstances , Return the same result every time , And it doesn't interact directly with the browser .
To interact with the browser , Please be there. componentDidMount() Or other life cycle methods . keep render() It's a pure function , Can make components easier to think about .
If shouldComponentUpdate() return false, Will not call render().
shouldComponentUpdate() {
console.log("shouldComponentUpdate");
return true; // Remember to return true
}
Copy code
shouldComponentUpdate() {
console.log("shouldComponentUpdate");
return false; // Remember to return true
}
Copy code
componentDidMount
componentDidMount() {
console.log("componentDidMount");
}
Copy code
componentDidMount() After the component is mounted ( Insert DOM In the tree ) Call immediately . Depend on DOM The initialization of the node should be here . If you need to request data through the network , This is a good place to instantiate requests .
This method is more suitable for adding subscriptions . If a subscription is added , Please don't forget to be in componentWillUnmount() Unsubscribe from
You can componentDidMount() Directly call setState(). It will trigger additional rendering , But this rendering happens before the browser updates the screen . This ensures that even in render() In the case of two calls , The user will not see the intermediate state .
componentDidMount() {
console.log("componentDidMount");
this.setState({
str: 'str'
})
}
Copy code
Why? componentDidMount Before the parent node ?
- Common sense understanding : The parent and child nodes are hung on dom node , If the child node is not mounted successfully , The parent component cannot indicate that it has been successfully mounted .
- Process description :Diff Algorithm ,render At the stage , For each node completeWork In the method , The current effectTag Node identification and establishment of linked list structure effectList.completeWork The nodes of are also executed from the child nodes up , So in render After the phase is successful , The root node will have an all ID effectTag List to be updated .firstEffectTag Point to the first leaf node that needs to be updated . This linked list is made here to optimize , You can't render Phase in diff Algorithm , here we are commit It needs to be done again diff Algorithm .
Updating
Update example
Manually update the parent component state
Manually update subcomponents state
Force update of parent component state
Force update of subcomponents state
shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
return true; // Remember to return true
}
Copy code
according to shouldComponentUpdate() The return value of , Judge React Whether the output of the component is subject to the current state or props The impact of the change . The default behavior is state Every time something changes, the component re renders . In most cases , You should follow the default behavior .
When props or state When something changes ,shouldComponentUpdate() Will be called before rendering execution . The return value defaults to true.
This method only exists as a way of performance optimization . Don't try to rely on this method “ prevent ” Rendering , Because it might produce bug. You should consider using the built-in PureComponent Components , Not by hand shouldComponentUpdate().PureComponent Would be right props and state Make shallow comparison , And reduces the possibility of skipping necessary updates .
If you have to write this function manually , Can be this.props And nextProps as well as this.state And nextState Compare , And back to false Informing React You can skip updates . Please note that , return false It does not prevent subcomponents from being in state Re render on change .
We do not recommend shouldComponentUpdate() To make a deep comparison or use JSON.stringify(). This has a great impact on efficiency , And it can damage performance .
Render or use... For the first time forceUpdate() This method will not be called when
The initial load :
forceUpdate:
return false
If shouldComponentUpdate() return false, Will not call UNSAFE_componentWillUpdate(),render() and componentDidUpdate()
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
return false; // Remember to return true
}
Copy code
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate() In the last render output ( Submitted to the DOM node ) Previous call . It allows components to change from DOM Capture some information in ( for example , Scroll position ). Any return value for this lifecycle will be passed as a parameter to componentDidUpdate().
This usage is not common , But it could be in UI In processing , If you need to deal with the chat thread in the scrolling position in a special way .
Should return snapshot Value ( or null).
This new update replaces componentWillUpdate. common componentWillUpdate Before the component is updated , Read the current DOM State of element , And in componentDidUpdate And deal with it accordingly .
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate");
return {
name: 'componentWillUpdate'
}
}
// componentWillUpdate(prevProps, prevState) {
// console.log("componentWillUpdate");
// return {
// name: 'componentWillUpdate'
// }
// }
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate");
debugger
console.log(snapshot)
}
Copy code
componentWillUpdate & getSnapshotBeforeUpdate The difference between
- stay React When asynchronous rendering mode is on , stay render Phase reads DOM The state of an element is not always related to commit Same stage , This leads to componentDidUpdate Use in componentWillUpdate Read from DOM Element states are unsafe , Because the value at this time is likely to be invalid .
- getSnapshotBeforeUpdate It will be in the end render Called before , That is to say getSnapshotBeforeUpdate Read from DOM The state of an element is guaranteed to be identical with componentDidUpdate In accordance with .
- Any value returned by this lifecycle will be passed as a parameter to componentDidUpdate().
componentDidUpdate
componentDidUpdate(prevProps, prevState, snapshot)
Copy code
componentDidUpdate() Will be called immediately after the update . The first rendering does not do this .
When the component is updated , You can do it here DOM To operate . If you are on the update before and after props Made a comparison , You can also choose to make a network request here .( for example , When props When there is no change , The network request will not be executed ). You can also be in componentDidUpdate() Call directly setState(), But notice that it has to be wrapped in a conditional statement , Deal with it as in the example above , Otherwise, it will lead to a dead cycle .
componentDidUpdate(prevProps) {
// characteristic use ( Don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
Copy code
getSnapshotBeforeUpdate & componentDidUpdate The order of
Look at the above manual update parent component state, Generated updates . The sequence of life cycle execution is as follows . here getSnapshotBeforeUpdate and componentDidUpdate The order of execution is separate , The same starts with sub components , Then go to the parent component .
This is from commit For the three stages of :commit There are three stages
- before mutation Stage : operation DOM Before 【getSnapshotBeforeUpdate】
- mutation Stage : operation DOM【componentWillUnmount】
- layout Stage : operation DOM after 【componentDidMount,componentDidUpdate】
And above Why? componentDidMount Before the parent node ? The same thing ,commit The main stage is traversal render Stage formation effectList. because effectList The order of is from the leaf node . So the order here is from child node to parent node . So why is it divided into two batches . Because these two methods are in commit Performed at different stages of .
Unmounting
componentWillUnmount
componentWillUnmount()
Copy code
componentWillUnmount() It will be called directly before the component is unloaded and destroyed . Perform the necessary cleanup operations in this method , for example , eliminate timer, Cancel network request or clear in componentDidMount() Subscriptions created in, etc .
componentWillUnmount() Should not call setState(), Because the component will never re render . After the component instance is uninstalled , Will never mount it again .
Uninstall components
The reason here is that when the parent node is diff Algorithm time , Identify that the current node needs to be deleted , The continuous traversal of the current node will be ended . And in the commt Of mutation Stage , Conduct delete case When , It will traverse the parent node and all child nodes , Remove for delete operation .
performance optimization
React.PureComponent
React.PureComponent And React.Component Very similar . The difference between the two is React.Component Did not achieve shouldComponentUpdate(), and React.PureComponent In contrast to the shallow prop and state This function is implemented in the following way .
If given React Components are the same props and state,render() Function will render the same thing , So in some cases, use React.PureComponent Improve performance .
React.memo
React.memo For higher-order components .
If your components are in the same props Render the same result , Then you can pack it in React.memo Call in , In this way, the performance of components can be improved by memorizing the rendering results of components . This means that in this case ,React The operation of the rendering component will be skipped and the result of the last rendering will be reused directly .
React.memo Check only props change . If the function component is React.memo The parcel , And its implementation has useState,useReducer or useContext Of Hook, When state or context When something changes , It will still re render .
By default, it only performs shallow contrast on complex objects , If you want to control the contrast process , Please pass in the second parameter to implement the custom comparison function .