Life cycle overview

The projects. Wojtekmaj. Pl/react – lifec…

The lifecycle of the yellow box is the lifecycle function that was removed in Act17.0

Life cycle specification

/* 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 child component state");
    this.childRef.current.setTheState();
  }

  forceLifeCycleUpdate() {
    console.info("Force update child component");
    this.childRef.current.forceItUpdate();
  }

  parentForceUpdate() {
    console.info("Force update parent component");
    this.forceUpdate();
  }

  render() {
    console.log("parent render")

    return (
      <div>
        <button
          className="weui_btn weui_btn_primary"
          onClick={this.propsChange.bind(this)}
        >Update the parent component state</button>
        

        <button
          className="weui_btn weui_btn_primary"
          onClick={this.setLifeCycleState.bind(this)}
        >Update child component state</button>
        

        <button
          className="weui_btn weui_btn_primary"
          onClick={this.forceLifeCycleUpdate.bind(this)}
        >ForceUpdate subcomponents</button>
        

        <button
          className="weui_btn weui_btn_primary"
          onClick={this.parentForceUpdate.bind(this)}
        >Father forceUpdate components</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 the code

Monting phase

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 the code

If state is not initialized or method binding is not performed, there is no need to implement a constructor for the React component.

Before the React component is mounted, its constructor is called. When implementing a constructor for a react.componentsubclass, you should call super(props) before any other statements. Otherwise, this. Props might have undefined bugs in the constructor.

In general, constructors in React are used only in two cases:

  • Initialize internal state by assigning an object to this.state.
  • Bind the instance to the event handler function

Do not call the setState() method from the constructor() function. If your component needs to use internal state, assign the initial state to this.state directly in the constructor:

Avoid copying the value of props to state

constructor(props) {
 super(props);
 // Don't do that
 this.state = { color: props.color };
}
Copy the code

When the props update is not updated to state. Only use it if you deliberately ignore prop updates. At this point, you should rename prop to initialColor or defaultColor. If necessary, you can change its key to force a “reset” of its internal state.

getDerivedStateFromProps

GetDerivedStateFromProps is called before the Render method is called, and is called both during the initial mount and during subsequent updates. It should return an object to update state, and if null is returned, nothing is updated.

  static getDerivedStateFromProps(nextProps, prevState) {
      console.log("getDerivedStateFromProps");
      return {str: "getDerivedStateFromProps update state"};
  }
Copy the code

This method does not have access to the component instance. If you need to, you can reuse the code between getDerivedStateFromProps() and other class methods by extracting pure functions of the component props and state outside the class. This is undefined.

Note that this method fires before every render, regardless of the cause. With the UNSAFE_componentWillReceiveProps contrast, the latter only trigger when parent component rendering, rather than the internal call setState.

getDerivedStateFromProps

UNSAFE_componentWillReceiveProps

ComponentWillReceiveProps can access component instances, this is the current component

Because this method is called every render for whatever reason, null is returned by default. If the props pass does not need to affect your state, then you need to return null, which 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 the 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 the code

render

The Render () method is the only one that must be implemented in the class component.

When render is called, it checks for changes to this.props and this.state and returns one of the following types:

  • The React elements. Typically created through JSX. For example,
    React renders it as a DOM node, and React renders it as a custom component, either

    React elements again.

  • Arrays or fragments. Enables the Render method to return multiple elements. For more details, see the Fragments document.
  • Portals. You can render child nodes into different DOM subtrees. For more details, see the documentation on Portals
  • A string or numeric type. They are rendered as text nodes in the DOM
  • Boolean type or NULL. Nothing is rendered. (Mainly used to support patterns that return test &&, where test is a Boolean type.)

The render() function should be pure

This means that the same result is returned each time it is called without changing the component state, and it does not interact directly with the browser.

To interact with the browser, perform your actions in componentDidMount() or another lifecycle method. Keeping Render () pure makes the component easier to think about.

If shouldComponentUpdate() returns false, render() is not called.

  shouldComponentUpdate() {
      console.log("shouldComponentUpdate");
      return true;        // Remember to return true
  }
Copy the code

  shouldComponentUpdate() {
      console.log("shouldComponentUpdate");
      return false;        // Remember to return true
  }
Copy the code

componentDidMount

componentDidMount() {
      console.log("componentDidMount");
  }
Copy the code

ComponentDidMount () is called immediately after the component is mounted (inserted into the DOM tree). Initialization dependent on DOM nodes should be placed here. This is a good place to instantiate a request to get data over the network.

This method is a good place to add subscriptions. If you add a subscription, don’t forget to unsubscribe at componentWillUnmount()

You can call setState() directly in componentDidMount(). It triggers an additional rendering, but before the browser updates the screen. This ensures that the user will not see the intermediate state even if render() is called twice.

  componentDidMount() {
      console.log("componentDidMount");
      this.setState({
        str: 'str'})}Copy the code

Why does componentDidMount execute before the parent?

  • A parent node is mounted on a DOM node. If the child node is not successfully mounted, the parent component cannot indicate that it is successfully mounted.
  • Process description: Diff algorithm, in the render stage, the completeWork method of each node will carry out the identification of the current effectTag node and establish the linked list structure effectList. The nodes of the completeWork are also executed from the child nodes up, so after the render phase succeeds, the root node has a list of all identified EffectTags that need to be updated. FirstEffectTag points to the first leaf node that needs to be updated. The purpose of this list is to optimize it. Diff algorithm cannot be performed in the render stage, and diff algorithm needs to be performed again at commit.

Updating

Update example

Manually update the parent component state

Manually update the child component state

Force an update of the parent component state

Force an update of child component state

shouldComponentUpdate

  shouldComponentUpdate(nextProps, nextState) {
      console.log("shouldComponentUpdate");
      return true;        // Remember to return true
  }
Copy the code

Check whether the output of the React component is affected by changes to the current state or props based on the return value of shouldComponentUpdate(). The default behavior is that the component is rerendered every time the state changes. For the most part, you should follow the default behavior.

ShouldComponentUpdate () is called before rendering when props or state changes. The default return value is true.

This approach exists only as a way to optimize performance. Do not attempt to rely on this method to “block” rendering, as this can be buggy. You should consider using the built-in PureComponent instead of writing shouldComponentUpdate() manually. The PureComponent makes a shallow comparison between props and state and reduces the possibility of skipping necessary updates.

If you must write this function manually, compare this.props to nextProps and this.state to nextState and return false to tell React to skip the update. Note that returning false does not prevent child components from rerendering when state changes.

We don’t recommend deep comparisons or using json.stringify () in shouldComponentUpdate(). This is very inefficient and can hurt performance.

This method is not called the first time you render or use forceUpdate()

Initial loading:

ForceUpdate:

Returns false

If shouldComponentUpdate() returns false, UNSAFE_componentWillUpdate(), render(), and componentDidUpdate() are not called.

  shouldComponentUpdate(nextProps, nextState) {
      console.log("shouldComponentUpdate");
      return false;        // Remember to return true
  }
Copy the code

getSnapshotBeforeUpdate

GetSnapshotBeforeUpdate () is called before the last render output (submitted to the DOM node). It enables the component to capture some information (for example, scroll position) from the DOM before changes are made. Any return value for this life cycle is passed as an argument to componentDidUpdate().

This usage is not common, but it can occur in UI processing, such as chat threads that need to handle scrolling positions in a special way.

The value of snapshot should be returned (or NULL).

This new update replaces componentWillUpdate. A common use case for componentWillUpdate is to read the current state of a DOM element and process it in componentDidUpdate before the component is updated.


  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 the code

ComponentWillUpdate & getSnapshotBeforeUpdate difference

www.jianshu.com/p/b91e95af2…

  • When asynchronous render mode is enabled in React, the DOM element states read in render are not always the same as those read in commit. This makes it unsafe to use the DOM element states read in componentWillUpdate in componentDidUpdate because the values are likely to be invalid.
  • GetSnapshotBeforeUpdate is called before final render, which means that the DOM element states read in getSnapshotBeforeUpdate are guaranteed to be the same as in componentDidUpdate.
  • Any values returned by this life cycle are passed as arguments to componentDidUpdate ().

componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot)
Copy the code

ComponentDidUpdate () is called immediately after the update. This method is not performed for the first rendering.

This is where you can manipulate the DOM when the component is updated. If you compare the props before and after the update, you can also do the network request here. (For example, network requests are not executed when the props are not changed). You can also call setState() directly in componentDidUpdate(), but note that it must be wrapped in a conditional statement, as in the example above, otherwise it will cause an infinite loop.

componentDidUpdate(prevProps) {
  // Typical usage (don't forget to compare props) :
  if (this.props.userID ! == prevProps.userID) {this.fetchData(this.props.userID); }}Copy the code

GetSnapshotBeforeUpdate & componentDidUpdate Order

See above for the updates generated by manually updating the parent component state. The lifecycle is executed in the following order. Here getSnapshotBeforeUpdate and componentDidUpdate are executed separately, again starting from the child component and then to the parent component.

There are three stages of commit

  • Before mutation stage: getSnapshotBeforeUpdate
  • DOM (componentWillUnmount)
  • Layout stage: componentDidMount componentDidUpdate

Why does componentDidMount execute before the parent node? As always, the Commit phase is essentially iterating through the render phase to form an effectList. Because the order of the effectList starts with the leaf node. So the order here is from child to parent. So why split into two batches. Because these two methods are executed at different stages of the COMMIT.

Unmounting

componentWillUnmount

componentWillUnmount()
Copy the code

ComponentWillUnmount () is called directly before the component is unmounted and destroyed. Perform necessary cleanup actions in this method, such as clearing timers, canceling network requests, or clearing subscriptions created in componentDidMount().

SetState () should not be called in componentWillUnmount() because the component will never be re-rendered. Once a component instance is unmounted, it will never be mounted again.

Uninstall the component

The reason is that when the parent node performs the diff algorithm, it indicates that the current node needs to be deleted, thus ending the continuous traversal of the current node. In the mutation stage of CommT, during the DELETE case, the parent node was traversed through all the child nodes, and the delete operation was performed after removal.

Performance optimization

React.PureComponent

React.PureComponent is very similar to react.component.react.component.react.pureComponent. The difference is that shouldComponentUpdate() is not implemented in react.pureComponent, which is implemented in a shallow way to compare prop and state.

If you give the React component the same props and state, and the render() function renders the same content, using the React.PureComponent can improve performance in some cases.

React.memo

React.memo is a higher-order component.

If your component is rendering the same props, you can improve the performance of the component by wrapping it in a react.Memo call to remember the component’s rendering results. This means that in this case React will skip the render component and simply reuse the results of the last render.

React.memo only checks the props changes. If a function component is wrapped in react. Memo and its implementation has a Hook for useState, useReducer, or useContext, it will still rerender when state or context changes.

By default, only shallow comparisons are performed on complex objects. If you want to control the comparison process, pass in your custom comparison function as a second argument.