preface

Before learning about the principles of any framework, it is essential to learn about its lifecycle, at least so that we know what to do and when. Don’t make stupid mistakes.

React 16.8 released the Hook mechanism, so this article will explain the Class Component lifecycle and the Function Component lifecycle separately.

Class Component lifecycle

The React life cycle refers to the entire process from component creation to uninstallation. Each process has corresponding hook functions called. The React life cycle consists of the following phases:

  • Mount stage – the process by which a component instance is created and inserted into the DOM tree
  • Update phase – the process by which components are re-rendered
  • Unload phase – the process by which a component is removed from the DOM tree

Mount the stage

This stage is to do the initialization operation, mainly the following hook functions:

static defaultProps

Set the default value of props

static defaultProps = {
  name: 'Default props for child component Settings'
}
Copy the code

static propTypes 

Props Data type check

import PropTypes from 'prop-types';

static propsTypes = {
  name: PropTypes.string // qualify name to string
}
Copy the code

constructor(props) 

The constructor does:

  • Initialize theprops and state
  • Bind event handlers
constructor(props) {
  super(props);
  this.state = {number: 0};
  this.handlexxx = this.handlexxx.bind(this)}Copy the code

componentWillMount() 

Component mount front hook **(not recommended after version 17)**

With asynchronous rendering introduced in future versions of React, the DOM can be interrupted and restarted before being mounted, Cause componentWillMount, componentWillUpdate, componentWillReceiveProps during an update may be triggered multiple times, So side effects that you only want to fire once should be placed in componentDidMount.

That’s why you put asynchronous requests in componentDidMount, not componentWillMount, for backward compatibility.

componentWillMount() {
	// componentWillMount may be executed multiple times during rendering
}
Copy the code

componentDidMount() 

The process component has been successfully mounted to the real DOM.

Since it is executed only once during rendering, it is often used to:

  • Listening event;
  • Get the real DOM;
  • Request the background interface.
componentDidMount(){
  fetch('https://api.github.com/users').then(res= >res.json()).then(users= >{
    console.log(users);
    this.setState({users});
  });
}
Copy the code

Update the stage

This stage is mainly to do the status update operation, mainly has the following hook functions:

componentWillReceiveProps(newProps) 

Parent component updates the props hook **(not recommended after version 17)**

shouldComponentUpdate(nextProps, nextState)  

Whether the component updates the hook

When the React parent component is updated, the child component must be updated. Therefore, we can manually compare the props and nextProps and the state and nextState in the child component to determine whether the child component needs to be re-rendered. If the child component needs to be re-rendered, return true; if it does not, return false. This function returns true by default.

 shouldComponentUpdate(nextProps, nextState) {
   console.log('Counter', nextProps, nextState);
   console.log('5. ShouldComponentUpdate asks if component needs to be updated ');
   return true;
 }
Copy the code

componentWillUpdate()

Component update pre-hook **(not recommended after version 17)**

componentDidUpdate() 

This lifecycle method is called after the component has been updated.

  • Because the component has been rerendered, you can manipulate the DOM in the component;
  • In the comparison of thethis.props 和 nextPropsCan send network request under the premise of.
componentDidUpdate(prevProps, prevState, snapshot) {
	if (this.props.userID ! == prevProps.userID) {this.fetchData(this.props.userID); }}Copy the code

Unloading phase

This stage is primarily the operation of removing a component from the DOM tree, and its hook has only one componentWillUnmount.

componentWillUnmount() 

This is the only life cycle of the unmount phase where the cleanup takes place: clearing timers, canceling network requests, or canceling event listening, etc.

Older version execution order graph

17 Release lifecycle

Two new lifecycle functions have been added:

  1. static getDerivedStateFromProps(nextProps, prevState)
  2. getSnapshotBeforeUpdate(prevProps, prevState)

The following lifecycle functions have been removed:

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

Starting with this release, only the new UNSAFE_ lifecycle name will work:

  1. UNSAFE_componentWillMount
  2. UNSAFE_componentWillReceiveProps
  3. UNSAFE_componentWillUpdate

static getDerivedStateFromProps(nextProps, prevState)

Called before Render, at initial mount, and at subsequent updates.

He should return an object to update state. If null is returned, nothing is updated.

It takes two arguments, nextProps passed in and prevState previously.

static getDerivedStateFromProps(nextProps, prevState){
  console.log('getDerivedStateFromProps',nextProps,prevState);
  return null;
}
Copy the code

getSnapshotBeforeUpdate(prevProps, prevState)

Action taken after the render update phase and before mounting to the real DOM, which allows the component to capture information from the DOM before changes are made. Any value returned by this component will be the third argument to componentDidUpdate.

  getSnapshotBeforeUpdate(prevProps, prevState){
    return "getSnapshotBeforeUpdate";
  }

  // Component update success hook
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log(snapshot); // "getSnapshotBeforeUpdate"
  }
Copy the code

17 Version Execution sequence Map:

Life cycle site query link

That’s the Class component’s lifecycle functions and how to use them, but no matter how useful the Class component is, we’ve embraced Hooks.

Function Component lifecycle

Function Component is a more thorough state-driven abstraction. It doesn’t even have the concept of a Class Component lifecycle, just one state, whereas React takes care of synchronizing to the DOM.

Review the data request in Class Component:

  1. incomponentDidMountInitializing send request;
  2. incomponentDidUpdateDetermine whether the parameters change, change to call the request function to request data;
  3. incomponentWillUnmountThe life cycle cancels sending the request.

So what do we do in function components? The answer is useEffect.

useEffect

UseEffect is an Effect Hook that gives function components the ability to manipulate side effects. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in the Class component, but has been consolidated into an API.

UseEffect does:

  • useuseEffectThe React component is told that it needs to do something after rendering, and React will call it after performing a DOM update.
  • React guarantees every runuseEffectThe DOM has been updated.

Class component Demo:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked The ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked The ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={()= > this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>); }}Copy the code

Function Component rewrites this case:

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  ComponentDidMount and componentDidUpdate:
  useEffect(() = > {
    // Update the page title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()= > setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
Copy the code

Effects that need to be cleared

In the Class component, when we listen for native DOM events, we do so during the componentDidMount lifecycle, because that’s where we get the real DOM that’s already mounted. We also disable event listening during component uninstallation to avoid memory leaks. So how do you implement this in useEffect?

useEffect(() = > {
  function handleClick(status) {
    document.title = `You clicked ${count} times`;
  }
  
  document.body.addEventListener("click",handleClick,false);
 
  return function cleanup() {
    document.body.removeEventListener("click",handleClick,false);
  };
});
Copy the code

It cleans up the side effects by returning a function in useEffect.

The cleanup rules are:

  • The first render will not be cleaned up, the next render will remove the last side effects;
  • The unload phase also performs a cleanup operation.

Dependencies

In useEffect, we will request background data. From the previous study, we also know that useEffect will be executed again every time the component is updated, but in fact, we do not need to send a request every time the component is updated. So how to deal with such a problem?

Is this similar to sending requests in componentDidUpdate? The intuition is right. In componentDidUpdate, we avoid sending requests every time by determining whether the parameters change, so in useEffect Hook, we also do the same. We decide whether to repeat the execution by determining whether the second parameter changes. The command is executed only once during initialization and will not be invoked again for subsequent updates.

useEffect(() = > {
  fetchData(instanceId){... } fetchData(instanceId); }, [instanceId]);Copy the code

In the example above, fetchData is used to request background data. The body of the function is omitted. Then you can see that the second argument to useEffect adds an array to which instanceId is added, indicating that only when the instanceId changes, Let’s execute useEffect again. This saves us from making multiple requests for background data.

Of course our dependency can also be passed an empty array to indicate that it is initialized only once:

useEffect(() = > {
  fetchData(instanceId){...}
  
  fetchData(instanceId);
}, []);
Copy the code

useCallback

const memoizedCallback = useCallback(
  () = > {
    doSomething(a);
  },
  [a],
);
Copy the code

Passing the inline callback function and the array of dependencies as arguments to useCallback returns the Memoized version of the callback function, which is updated only when a dependency changes.

In layman’s terms, when parameter A changes, a new function reference is returned to memoizedCallback variable, so this variable can be used as useEffect second parameter. This effectively separates the logic.

function Parent(){
	const [query,setQuery] = useState('q');
  const fetchData = useCallback(() = >{... },[query]);return <Child fetchData={fetchData} />
}
  
function Child({fetchData}){
  const [data,setData] = useState(null);
	useEffect(() = >{
  	fetchData().then(setData);
  },[fetchData])
}
Copy the code

UseCallback wrapped functions can be used as normal variables as useEffect dependencies. All useCallback does is return a new function reference when its dependency changes, trigger the useEffect dependency change, and activate its re-execution.

Now we don’t need to compare query parameters directly in useEffect dependencies, but fetchData functions directly. UseEffect As long as you care about whether the fetchData function changes, and fetchData parameter changes are concerned when useCallback, it can achieve dependency non-loss, logical cohesion, and easy maintenance.

summary

This article explains the life cycle functions of Class Component and Function Component respectively, so that we can have a deeper understanding of how React works. And life cycle is also a common interview question, so we need to master.