preface

How to build a tall building without a solid chassis. Just like shenzhen Huaqiang North Seg Tower tilting but not falling, fluttering in the wind.

Today’s focus is on the React lifecycle

It is simply the chassis of the chassis, life is gone, how to survive.

Let’s look at a simple example:

import React, { Component } from "react";

class Index1 extends Component {
  state = {
    opacity: 1}; timer: number |undefined;
  componentDidMount() {
    console.log("componentDidMount");
    this.timer = setInterval(() = > {
      let { opacity } = this.state;
      opacity -= 0.1;
      if (opacity <= 0) opacity = 1;
      this.setState({ opacity });
    }, 200);
  }

  // The component will be unmounted
  componentWillUnmount(){
    console.log("componentWillUnmount")
    // Clear the timer
    clearInterval(this.timer)
  }

  render(): React.ReactNode {
    return (
      <>
        <h2 style={{ opacity: this.state.opacity}} >Michelle Reis came to your house</h2>
      </>); }}export default class example12 extends Component {
  state = {
    status: true}; changeIndex =() = > {
    this.setState({ status:!this.state.status });
  };
  render() {
    return (
      <>
        <h2>The life cycle</h2>
        {this.state.status ? <Index1></Index1> : ""}

        <button onClick={this.changeIndex}>Michelle Reis was gone</button>
      </>); }}Copy the code

Effect:

ComponentDidMount is executed for component rendering and componentWillUnmount is executed for component unmounting.

Is there any other life cycle? There is.

Life cycle (old)

The life cycle diagram before React 16 looked like this:

Here’s a simple example:

import React, { Component } from "react";

class Index1 extends Component<{ death: () = > void; carName: string }, { count: number }> {
  constructor(props: { death: () => void; carName: string } | Readonly<{ death: () => void; carName: string }>) {
    console.log("Count---constructor");
    super(props);
    // Initialization state
    this.state = { count: 0 };
  }
  add = () = > {
    // Get the original state
    const { count } = this.state;
    // Update the status
    this.setState({ count: count + 1 });
  };
  force = () = > {
    this.forceUpdate();
  };
  // The hook to be mounted by the component
  componentWillMount() {
    console.log("Count---componentWillMount");
  }

  // The hook that the component will finish mounting
  componentDidMount() {
    console.log("Count---componentDidMount");
  }

  // The hook that the component will unload
  componentWillUnmount() {
    console.log("Count---componentWillUnmount");
  }

  // Control assembly thinner valve
  shouldComponentUpdate() {
    console.log("Count---shouldComponentUpdate");
    return true;
  }

  // The hook that the component will update
  componentWillUpdate() {
    console.log("Count---componentWillUpdate");
  }

  // The component's updated hook
  componentDidUpdate() {
    console.log("Count --- componentDidUpdate");
  }
  render(): React.ReactNode {
    console.log("Count---render");
    const { death, carName } = this.props;

    const { count } = this.state;
    return (
      <>
        <h3>The current sum is: {count}</h3>
        <h3>I'm going to open: {carName}</h3>
        <button onClick={this.add}>I + 1 point</button>
        <button onClick={()= >Death ()}> Uninstall the component</button>
        <button onClick={this.force}>Force an update without changing any data in the state</button>
      </>); }}export default class example12 extends Component {
  state = {
    status: true.carName: "Mercedes"}; changeIndex =() = > {
    this.setState({ status:!this.state.status });
  };
  changCar = () = > {
    this.setState({ carName: "Rolling" });
  };
  render() {
    console.log("Render of parent component");
    return (
      <>
        <h2>The life cycle</h2>
        {this.state.status ? <Index1 death={this.changeIndex} carName={this.state.carName}></Index1> : ""}

        <hr />
        <div>
          <button onClick={this.changCar}>To change</button>
        </div>
      </>); }}Copy the code

Effect:

The following points can be summarized from the above:

  1. Initialization stage: first render triggered by parent render()
    • constructor()
    • componentWillMount()
    • render()
    • OmponentDidMount () =====> This hook is used to do some initialization, such as start timer, send network request, subscribe message
  2. Update phase: triggered by the component’s internal this.setsate () or parent’s Render
    • shouldComponentUpdate()
    • componentWillUpdate()
    • Render () =====> One that must be used
    • componentDidUpdate()
  3. Unload component: Triggered by the parent component’s changeIndex event
    • ComponentWillUnmount () =====> componentWillUnmount() : componentWillUnmount() =====> componentWillUnmount()

If you’re curious, you’ll notice some warnings on the right:

Here’s the official explanation:

React is ready to enable these apis, prefix them with UNSAFE_ if you need to use them

However, the warning will still be issued after the change:

This basically means UNSAFE_componentWillMount in strict mode. Hey, when did we turn on strict mode? The use of Vite to create projects automatically adds:

As long as theReact.StrictModeComments will do. More canReact explains this

After looking at the old, let’s look at the new

Life cycle (New)

1. What are the life cycles?

React 16 the React lifecycle is as follows:

Summarize according to the picture above:

Divide the component lifecycle into three phases:

  • Mount, the process by which a component is first rendered in the DOM tree;
  • Update the rendering process when the state of the component changes.
  • Unmount, the process by which components are removed from the DOM tree;

1) Component mounting stage

In the mount phase, the component is created and then the component instance is inserted into the DOM to complete the first rendering of the component. This process occurs only once, during which the following methods are called in sequence:

  • constructor
  • getDerivedStateFromProps
  • render
  • componentDidMount
(1) constructor

The constructor of the component, the first one to be executed, has a default constructor if it is not explicitly defined, but if the constructor is explicitly defined, we must call super(props) in the constructor or we cannot get this in the constructor.

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

Constructor usually does only two things:

  • Initialize the state of the component
  • Bind this to the event handler, which is usually replaced by the arrow function
constructor(props: { death: () => void; carName: string } | Readonly<{ death: () => void; carName: string }>) { console.log("Count---constructor"); super(props); This. state = {count: 0}; this.add = this.add.bind(this); }Copy the code
(2) getDerivedStateFromProps
static getDerivedStateFromProps(props, state)
Copy the code

This is a static method, so you can’t use this in this function. There are two arguments: props and state, which refer to the new parameter received and the state object of the current component, respectively. This function returns an object to update the current state object, or null if no update is needed.

This function is called when loading, when new props are received, or when setState and forceUpdate are called. This can be used if you want to change state when you receive a new attribute.

import React, { Component } from "react"; class Index1 extends Component<{ count: number }> { state = { count: 0, }; static getDerivedStateFromProps(props, state) { if (props.count ! == state.count) { return { count: props.count, }; } return null; } add = () => { this.setState({ count: this.state.count + 1, }); }; Render () {return (<> <h1> Count: {this.state.count}</h1> <button onClick={this.add}> I +1</button> </>); } } export default class example14 extends Component { render() { return <Index1 count={10}></Index1>; }}Copy the code

You can now explicitly pass in count, but there’s a problem here. If you want to increment state.count by clicking on it, you’ll see that the value doesn’t change at all, just keep the value passed in by props. This is because setState and forceUpdate also trigger this lifecycle in the React 16.4^ version, so when the component’s internal state changes, the method is reconfigured and the state value is assigned to the props value. Therefore, an additional field is needed to record the props value, which will solve the above problem. Details are as follows:

. State = {count: 0, preCount: 0,}; static getDerivedStateFromProps(props, state) { if (props.count ! == state.preCount) { return { count: props.count, preCount: props.count, }; } return null; }... Everything else is the same. I left it outCopy the code
(3) the render

Render is the core method in React. A component must have this method, which renders the component based on its state and properties props. This function only does one thing, which is to return what needs to be rendered, so don’t do any other business logic inside this function. Usually calling this method returns one of the following types:

  • React elements: This includes the native DOM and React components;
  • Arrays and fragments: Multiple elements can be returned;
  • Portals (slots) : The ability to render child elements to different DOM subtrees
  • Strings and numbers: rendered as text nodes in the DOM;
  • Boolean or NULL: Nothing is rendered.
(4) componentDidMount ()

ComponentDidMount () is called immediately after the component is mounted (inserted into the DOM tree). This phase usually performs the following operations:

  • Perform DOM-dependent operations;
  • Sending network requests; (Official recommendation)
  • Add a subscription message (unsubscribe at componentWillUnmount)

Calling setState in componentDidMount triggers an extra render, an extra call to the Render function, which the user isn’t aware of because it’s executed before the browser has refreshed the screen, but I should avoid it because it causes performance issues. Try to initialize the state object in Constructor.

After the component loads, change the count number to 1:

. ComponentDidMount () {this.setState({count: 1})}...... Everything else is the same. I left it outCopy the code

2) Component update stage

Update re-rendering is triggered when the props of the component changes, or when setState/forceUpdate is called inside the component, which can happen multiple times. This phase calls the following methods in sequence:

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate
(1) shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState)
Copy the code

Before we get to the life cycle function, let’s look at two questions:

  • Does the setState function cause the component to be rerendered in any case? For example:
this.setState({number: this.state.number})
Copy the code
  • If setState is not called and the props value does not change, does the component not rerender?

The answer to the first question is yes. The second question is if the parent component rerenders, regardless of whether the props passed in are changed, it will cause the child component to rerender.

Is there a way to improve performance in both scenarios without having components re-rendered? ShouldComponentUpdate () ¶ shouldComponentUpdate () ¶ shouldComponentUpdate () ¶ This is a lifecycle function that is used to speed up the rendering of components before they start. By default, it returns true. Whether the this.state and nextState values have changed to confirm whether to return true or false. When false is returned, component updating stops and subsequent render, componentDidUpdate is not called.

Note: When adding the shouldComponentUpdate method, it is not recommended to use depth equality checking (such as using json.stringify ()), as deep comparisons are inefficient and may be less efficient than new rendering components. In addition, this method is difficult to maintain. Therefore, you are advised to use this method when it can significantly improve performance.

(2) getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(prevProps, prevState)
Copy the code

PrevProps (); prevState (); prevProps (); prevProps (); prevState (); This function must be used with componentDidUpdate and must have a return value, null by default, which is passed to componentDidUpdate as the third argument.

(3) the componentDidUpdate

ComponentDidUpdate () is called immediately after the update, not the first rendering. This phase usually performs the following operations:

  • When the component is updated, the DOM is manipulated;
  • 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).
componentDidUpdate(prevProps, prevState, snapshot){}
Copy the code

This method takes three parameters:

  • PrevProps: props before the update
  • PrevState: Indicates the state before update
  • Snapshot: getSnapshotBeforeUpdate() Return value of the life cycle

3) Component unloading stage

The uninstall phase has only one lifecycle function, componentWillUnmount(), which is called directly before the component is unmounted and destroyed. Perform the necessary cleanup actions in this method:

  • Clear timer, cancel network request or clear
  • Unsubscribe subscriptions created in componentDidMount(), etc.

This life cycle is called before a component is unloaded and destroyed, so you should not use setState in this method, because once the component is unloaded, it is not loaded again and will not be re-rendered.

4) Error handling phase

ComponentDidCatch (Error, info), which is called after a descendant component throws an error. It receives two arguments:

  • Error: A thrown error.
  • Info: Object with componentStack key, which contains stack information about component raising errors

React’s common lifecycle is as follows: The React lifecycle process is as follows:

  • The mount phase starts with the constructor constructor method to create the component
  • Once created, the Render method is executed, which returns what needs to be rendered
  • React then mounts the content that needs to be rendered into the DOM tree
  • Once the mount is complete, the componentDidMount lifecycle function is executed
  • The Render function is recalled if we create a props (for component communication), call setState (to change data in state), or call forceUpdate (to force an update to the component) for the component
  • After the render function is re-executed, the DOM tree is re-mounted
  • Once the mount is complete, the componentDidUpdate lifecycle function is executed
  • When a component is removed, the componentWillUnmount life cycle function is executed

React lifecycle Summary:

  1. GetDefaultProps: This function is called once (and only once) before the component is created, and it is used to initialize the Props of the component;
  2. GetInitialState: State value used to initialize the component;
  3. ComponentWillMount: After component creation, before render, the componentWillMount phase is reached. I personally have not used this stage, very weak. React does not recommend you to do anything in componentWillMount. Now act16 has abolished the life cycle directly.
  4. Render: This is the only method of all life cycles that you have to implement. React renders the components to the interface based on props and state. Sometimes, however, you may not want to render anything, in which case it will return null or false;
  5. ComponentDidMount: Called immediately after the component is mounted (inserted into the DOM tree) to signal that the component is mounted. Some operations that depend on retrieving DOM node information are done in this phase. In addition, this is the React official recommended time to initiate Ajax requests. This method, like componentWillMount, has one and only one call.

2. What life cycles does React deprecate? Why is that?

The three deprecated functions were all prior to render, and due to the advent of fBER, it is likely that they will be executed multiple times due to the interruption of existing tasks due to the advent of higher priority tasks. Another reason is that React wants to constrain users. A good framework can force people to write code that is easy to maintain and extend

1) componentWillMount

This function can be replaced by using componentDidMount and Constructor, which are described above. If the method is removed asynchronously, the rest is just initialization. If you subscribe to events in willMount, but on the server it doesn’t execute willUnMount events, which means that the server is leaking memory so componentWilIMount can be completely unused, React removes this API in order to restrain developers

2) componentWillReceiveProps

In older versions of React, there was never an elegant way to update state if a component’s own state was related to its props. Check whether the two props are the same in componentWilReceiveProps. If they are different, update the new props to the corresponding state. Doing so breaks the single source of state data, making the state of the component unpredictable and, on the other hand, increasing the number of component redraws. There are many similar business requirements, such as a list that can be swiped horizontally to directly locate a Tab based on a value passed in when the currently highlighted Tab clearly belongs to the list itself. To address these issues React introduced the first new lifecycle: getDerivedStateFromProps. It has the following advantages

  • GetDSFP is a static method. You can’t use this, which is a pure function. Developers can’t write side effects
  • The developers can only compare prevProps with prevState, not prevProps, ensuring a simple relationship between state and props and not having to deal with the prevProps being empty on the first render
  • Based on the first point, separating state changes (setState) from expensive operations (tabChange) makes it easier to perform render and commit phases or optimizations.

3) componentWillUpdate

Like componentWillReceiveProps, many developers will be according to the variation of props in componentWillUpdate to trigger some callback. However, either componentWilReceiveProps or componentWilUpdate can be called multiple times in an update, so the callback function written here can be called multiple times, which is obviously not recommended. Like the componentDidMount class, componentDidUpdate is called only once in an update. Moving the callback originally written in componentWillUpdate to componentDidUpdate solves this problem.

In the other case, you need to get the DOM element state, but since render breaks in FBER, the element state you might get in wilMount might be different from what you actually need. This can usually be resolved using the second new life function getSnapshotBeforeUpdate(prevProps, prevState)

4) getSnapshotBeforeUpdate(prevProps, prevState)

The value returned as the third argument to componentDidUpdate. Unlike willMount, getSnapshotBeforeUpdate executes before the final render execution, ensuring that it gets the same element state as didUpdate. Reference code:

Import React, {Component} from "React "; import "./example16.css" class NewsList extends Component { list = React.createRef<HTMLDivElement>(); state = { newsArr: [] }; ComponentDidMount () {setInterval() => {const {newsArr} = this.state; Const news = "news" + (newsarr.length + 1); This.setstate ({newsArr: [news,...newsArr]}); }, 1000); } getSnapshotBeforeUpdate() { return this.list.current.scrollHeight; } componentDidUpdate(preProps, preState, height) { this.list.current.scrollTop += this.list.current.scrollHeight - height; } render() { return ( <div className="list" ref={this.list}> {this.state.newsArr.map((n, index) => { return ( <div key={index} className="news"> {n} </div> ); })} </div> ); } } export default class example16 extends Component { render() { return ( <> <NewsList></NewsList> </> ); }}Copy the code

Effect:

conclusion

The React lifecycle is here to stay. If you want to learn something new, you have to start with it. It’ll make it easier to get started.