This post is part of a series on Horseshoe React, with more to come

Come to my GitHub repo to read the full feature article

Come to my personal blog for an unparalleled reading experience

The life cycle, as the name suggests, is the process from birth to death.

And the lifecycle hooks are the key nodes in the process from birth to death.

What are the lifecycle hooks of the average person’s life?

  • born
  • To go to college
  • First Job
  • To buy a house
  • To get married
  • Having a child
  • The child’s life cycle hook
  • retired
  • Last words

At every critical juncture, we want to have moments of reflection where the decisions we make can change the course of our lives.

The React component, too, gives developers some reflective moments where they can change the direction of the component.

Life cycle under asynchronous rendering

React took two years to implement Fiber rendering.

In short, React calls the diff process Reconciliation. This process used to happen in one go, but Fiber makes it asynchronous. Asynchronous skills will be unlocked gradually in the next version.

How can it be asynchronous when it is clearly a piece of synchronous code?

The idea is that Fiber cuts tasks into tiny pieces and returns control to the main thread each time it executes one, waiting for the main thread to complete the remaining tasks. Of course, if the execution time of a particular section is too long (such as an infinite loop), then there is no main thread, it should crash.

What is the impact on the life cycle?

The impact is that the life cycle before mount and update becomes unreliable.

Why do you say so? Because the Reconciliation process can pause and then continue, lifecycle hooks before mounting and updating can either not execute or execute multiple times, and their behavior is unpredictable.

As a result, the React lifecycle has undergone a major overhaul, with the following lifecycle hooks being phased out:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

You see the features? They’re all hooks with Will.

React currently provides aliases for these lifecycle hooks:

  • UNSAFE_componentWillMount
  • UNSAFE_componentWillReceiveProps
  • UNSAFE_componentWillUpdate

Act17 will only provide aliases, completely discarding these three big boobs. That’s what it means to make you sick.

constructor()

React borrows the constructor of the class class to act as the initialization hook.

React does almost nothing, but since we only allow arguments to be passed to components in certain ways, constructor arguments are actually specified by React.

React specifies that constructor takes three arguments, namely props, context, and updater.

  • propsIt’s a property, it’s immutable.
  • contextIs the global context.
  • updaterIs an object that contains some update methods,this.setStateThe final call is going to bethis.updater.enqueueSetStateMethod,this.forceUpdateThe final call is going to bethis.updater.enqueueForceUpdateMethods, so these apis are mostly used internally in React, exposed for developers to use in a rainy day.

In React, since all class components inherit from the Component or PureComponent class, the constructor method needs to be called first to get this, just as with the native class.

The best practice for the Constructor lifecycle hook is to initialize this.state here.

Of course, you can use a property initializer instead, as follows:

import React, { Component } from 'react';

class App extends Component {
    state = {
        name: 'biu'}; }export default App;
Copy the code

componentWillMount()

💀 This is an API that React is no longer recommended.

This is the lifecycle hook before the component is mounted to the DOM.

Many people make the mistake that this hook is the best time to request data and then insert it into the element and mount it.

ComponentWillMount and mount are executed synchronously, meaning that the hook is mounted immediately after execution. Requests to the server for data are performed asynchronously. So no matter how fast the request is, it should be processed after the synchronization task, which is a generational issue.

That is, you can never insert data into an element here and mount it together.

It’s not that you can’t request data here, but it’s not as good as you think it is.

It was abandoned for two main reasons:

  • It was useless in the first place. I guess they created it to be a couple.
  • If it declares a timer or subscriber, in the server render,componentWillUnmountCleanup code in lifecycle hooks does not take effect. Because if the component is not successfully mounted,componentWillUnmountIt’s not going to happen. Yao Ming said: no mount, no unload.
  • It behaves erratically in asynchronous rendering.

The initialization of this.state should be done in the Constructor lifecycle hook, and the request data should be done in the componentDidMount lifecycle hook, so not only is it deprecated, it has no successor.

static getDerivedStateFromProps(props, state)

👽 This is the React V16.3.0 release API.

First, this is a static method lifecycle hook.

That is, you need to prefix the method with the static keyword or mount it directly to the class.

A brief distinction between instance methods and static methods:

  • Instance method, mounted inthisOn or mounted onprototypeOn, the class class does not have direct access to this method, usingnewAfter the keyword is instantiated, the instance can access the method.
  • Static methods, mounted directly on the class, or using new keywordsstaticInstance does not have direct access to the method.

The question is, why is the getDerivedStateFromProps lifecycle hook designed as a static method?

Then the developer can’t access this, which is the instance, and can’t call instance methods or setsState from it.

import React, { Component } from 'react';

class App extends Component {
    render() {
        return (
            <div>React</div>
        );
    }

    static getDerivedStateFromProps(props, state) {}
}

export default App;
Copy the code

The purpose of this lifecycle hook is to update its own state, called derived state, as needed, based on the props passed in by the parent component. The object returned is the state to increment.

It is designed to be static to keep the method pure, it is used to define derived state, and should not do anything else in it.

The lifecycle hook also went through a bit of a twist, as it was designed to fire during initialization, parent component updates, and receiving props, but now fires whenever rendered, i.e. during initialization and update.

render()

As a component, the core function is to mount elements to the DOM, so the Render lifecycle hook is a must.

How does the Render lifecycle hook receive the template? You return it, of course.

However, it is not recommended to write too much logic before return. If there is too much logic, it can be encapsulated as a function.

render() {
    // We can write some logic here
    return (
        <div>
            <input type="text" />
            <button>click</button>
        </div>
    );
}
Copy the code

Note that you should never call this.setState from the Render lifecycle hook, because this.setState will raise Render and it will never end. My Lord, there is a mole.

componentDidMount()

This is the lifecycle hook after the component is mounted to the DOM.

This is probably the most important lifecycle hook besides Render, because at this point all aspects of the component are ready for you.

That’s the social guy. He doesn’t talk too hard.

componentWillReceiveProps(nextProps)

💀 This is an API that React is no longer recommended.

ComponentWillReceiveProps lifecycle hooks only one parameter, the updated props.

The declared periodic function can be fired in two ways:

  • The component receives the new property.
  • The component does not receive new properties, but the current component is being rerendered due to the parent component’s rerendering.

Initialization does not trigger the lifecycle hook.

Also, because of the Fiber mechanism, this lifecycle hook can fire multiple times.

shouldComponentUpdate(nextProps, nextState)

The lifecycle hook is a switch that determines whether an update is needed, primarily to optimize performance.

With one exception, the React component ignores this hook if a developer calls this.forceUpdate to force an update.

The shouldComponentUpdate lifecycle hook returns true by default. That is, by default, the component will be updated whenever it triggers an update. React gives the developer control of judgment.

However, thoughtful React also provides a PureComponent base class that differs from the Component base class in that PureComponent automatically implements a shouldComponentUpdate lifecycle hook.

Components need to be rerendered only if their state changes. The shouldComponentUpdate lifecycle hook exposes two parameters. Developers can compare this.props and nextProps, this.state and nextState to determine if the state has changed, and return true or false accordingly.

When does the state not change but still trigger an update? Here’s an example:

The parent component passes a value to the child component, and when the parent component state changes, the child component is forced to update even if the value received by the child component does not change. This is obviously very unreasonable and React can’t do anything about it.

import React, { Component } from 'react';
import Child from './Child';

class App extends Component {
    state = { name: 'React'.star: 1 };

    render() {
        const { name, star } = this.state;
        return (
            <div>
                <Child name={name} />
                <div>{star}</div>
                <button onClick={this.handle}>click</button>
            </div>
        );
    }

	handle = () => {
        this.setState(prevState => ({ star: ++prevState.star }));
    }
}

export default App;
Copy the code
import React, { Component } from 'react';

class Child extends Component {
    render() {
        return <h1>{this.props.name}</h1>;
    }

    shouldComponentUpdate(nextProps, nextState) {
        if (this.props === nextProps) {
            return false;
        } else {
            return true; }}}export default Child;
Copy the code

Also watch out for reference type pits.

In this case, this. Props and nextProps can never be equal.

import React, { Component } from 'react';
import Child from './Child';

class App extends Component {
    state = { name: 'React'.star: 1 };

    render() {
        return (
            <div>
                <Child name={{ friend: 'Vue' }} />
                <div>{this.state.star}</div>
                <button onClick={this.handle}>click</button>
            </div>
        );
    }

	handle = () => {
        this.setState(prevState => ({ star: ++prevState.star }));
    }
}

export default App;
Copy the code
import React, { Component } from 'react';

class Child extends Component {
    render() {
        return <h1>{this.props.friend}</h1>;
    }

    shouldComponentUpdate(nextProps, nextState) {
        if (this.props === nextProps) {
            return false;
        } else {
            return true; }}}export default Child;
Copy the code

There are two solutions:

  • To comparethis.props.xxxandnextProps.xxx.
  • The reference type is cached with a variable in the parent component.

React returns a new object every time it updates state, rather than changing the original object.

componentWillUpdate(nextProps, nextState)

💀 This is an API that React is no longer recommended.

The shouldComponentUpdate lifecycle hook returns true or is executed immediately after this. ForceUpdate is called.

Note that the componentWillUpdate lifecycle hook is executed before every update, so calling this.setState here is dangerous and can go on forever.

Also, because of the Fiber mechanism, this lifecycle hook can be called multiple times.

getSnapshotBeforeUpdate(prevProps, prevState)

👽 This is the React V16.3.0 release API.

As the name suggests, it is used to save status snapshots.

This is called when the component is about to be mounted. It calls even later than Render, which shows that Render doesn’t finish mounting, but rather building the abstract UI. GetSnapshotBeforeUpdate immediately calls the componentDidUpdate lifecycle hook.

What is it used for? There are some states, such as the scroll position of the page, that I don’t need to persist, just to be able to revert to the original position when the component is updated.

The value returned by the getSnapshotBeforeUpdate lifecycle hook is received by the third parameter to componentDidUpdate. We can use this channel to store some state that does not need persistence, and then discard it.

Obviously, it replaces the componentWillUpdate lifecycle hook.

That’s right, developers don’t usually use it.

componentDidUpdate(nextProps, nextState, snapshot)

This is the lifecycle hook that fires after the component is updated.

When used with the getSnapshotBeforeUpdate lifecycle hook, the third argument is the return value of getSnapshotBeforeUpdate.

Similarly, the componentDidUpdate lifecycle hook executes after every update, so calling this.setState here is also dangerous and could go on forever.

componentWillUnmount()

This is the lifecycle hook before the component is unloaded.

Why do you need a moment of reflection when a component is about to uninstall?

Because developers need to wipe their asses.

As a best practice in React, event listeners, subscribers, and timers used in components are destroyed here.

By event listeners, OF course, I mean this:

componentDidMount() {
    document.addEventListener('click', () = > {}); }Copy the code

React will destroy itself.

render(
	return (
    	<button onClick={this.handle}>click</button>);Copy the code

componentDidCatch(error, info)

👽 This is the React V16.3.0 release API.

It is mainly used to catch errors and deal with them, so its usage is also special.

Customize an ErrorBoundary component with only componentDidCatch lifecycle hooks that does one thing: display an error message if an error is caught, and a child component if no error is caught.

Render the component that needs to catch the error as a sub-component of ErrorBoundary. Once the sub-component throws an error, the whole application will still not crash, but will be caught by ErrorBoundary.

import React, { Component } from 'react';

class ErrorBoundary extends Component {
    state = { hasError: false };

    render() {
        if (this.state.hasError) {
            return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
    }

    componentDidCatch(error, info) {
        this.setState({ hasError: true}); }}export default ErrorBoundary;
Copy the code
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import MyWidget from './MyWidget';

const App = (a)= > {
    return (
        <ErrorBoundary>
            <MyWidget />
        </ErrorBoundary>
    );
}

export default App;
Copy the code

The life cycle

With so many lifecycle hooks, there are really only three processes:

  • mount
  • update
  • uninstall

Mount and unload are performed only once, and updates are performed multiple times.

A complete React component lifecycle calls the following hooks in sequence:

old lifecycle

  • mount

    • constructor
    • componentWillMount
    • render
    • componentDidMount
  • update

    • componentWillReceiveProps
    • shouldComponentUpdate
    • componentWillUpdate
    • render
    • componentDidUpdate
  • uninstall

    • componentWillUnmount

new lifecycle

  • mount

    • constructor
    • getDerivedStateFromProps
    • render
    • componentDidMount
  • update

    • getDerivedStateFromProps
    • shouldComponentUpdate
    • render
    • getSnapshotBeforeUpdate
    • componentDidUpdate
  • uninstall

    • componentWillUnmount

Component tree lifecycle call stack

React first calls the Render hooks of the root component, and then the render hooks of the child components, if any, in recursive order.

By the time the render hooks of all components are recursively executed and the execution is in the hands of the last subcomponent, the next cycle of life cycle hooks is triggered, the last subcomponent’s componentDidMount hook is called, and the stack recurses up.

The lifecycle call stack of the component tree follows a zigzag pattern.

If the root component does not define an A lifecycle hook and the child component does, the call stack starts with the child’s A lifecycle hook.

In addition, whenever a lifecycle hook is defined within a component, it will execute even if it has no action.

app.render();
child.render();
grandson.render();
// divide
grandson.componentDidMount();
child.componentDidMount();
app.componentDidMount();
// divide
app.render();
child.render();
grandson.render();
// divide
grandson.componentDidUpdate();
child.componentDidUpdate();
app.componentDidUpdate();
Copy the code

ComponentWillMount, componentWillReceiveProps and componentWillUpdate lifecycle hook, of course, is likely to be interrupted, also may be called multiple times, performance is not stable. So React decided to phase them out.

However, it helps to understand React by knowing the normal order of calls throughout the application lifecycle.

React

What is the UI

JSX

State variable

Immutable property

The life cycle

component

The event

Operating the DOM

Abstract UI