React evolution of the life cycle of the road

React evolution of the life cycle



React 16.3 the previous life-cycle

  1. Initial loading assembly
    • constructor()
    • componentWillMount()
    • render()
    • componentDidMount()
  2. Run-time components
    • componentWillReceiveProps()
    • shouldComponentUpdate()
    • componentWillUpdate()
    • render()
    • componentDidUpdate()
  3. Component Uninstall
    • componentWillUnmount()

Why change?


It can be seen on a comprehensive, React lifecycle previous 16.3 very complete, covering the period of each component of life. Why change it? In fact, the main problem is performance, specifically for the following reasons

  1. js is single-threaded language, when the component tree too deep, time-consuming component updates every increase, blocking browser other actions, the formation of Caton
  2. Part of inexperienced programmers, incorrect use of the life cycle, resulting in abnormal procedures such as: Place asynchronous request event binding and function in componentWillMount, when server-side rendering component does not trigger repeat request componentWillUnmount lead to repeated listening, memory overflow.
  3. The main reason React After 17 enabled React Fiber start asynchronous rendering.

What is React Fiber?


React Fiber is what is it? The official explanation is that the word React Fiber is a re-implementation of the core algorithm . So it seems too vague, so we should talk about in detail.

First understand the limitations of previous React Fiber

In the existing React, the update process is synchronous, which can cause performance problems.

When React decide to load or update the component tree, it will do a lot of things, such as the life cycle of a function call to the individual components, calculation and comparison Virtual DOM, last updated DOM tree, this whole process is carried out simultaneously, which means that as long as a load or update process begins, you React to it does not break Loulan not also the spirit, big bang run in the end, will not stop halfway.

On the surface, this design is also very reasonable, because the update process will not have any I / O operations Well, is entirely CPU computing, there is no need asynchronous operation, and indeed just running all the way on the line, however, when relatively large component tree when the question came.

If a component needs update 1 millisecond, if there are 200 components to be updated, it would need 200 milliseconds, 200 milliseconds in this update, the browser that only concentrate on the main thread running update operation, no time to do anything else thing. Imagine that within 200 milliseconds, a user to input what elements in the input point, typing on the keyboard will not get a response, because the rendering work is also the result of input keys main thread of the browser, but the browser main thread is accounted for yet React not spare empty, the end result is that the user can not see the knock on the button reaction, such as the end of the update process after react, Kaka Ka those keys suddenly appear in the input element.

This is called interface Caton, very good user experience.

React existing version, there will be great when the component tree this problem, because the update process is one component synchronously sets of one component, the gradual deepening of the process, do not stop until you have updated all the components, functions call stack such as the following figure, calling very deep, and will not return for a long time.

Because the characteristics of single-threaded JavaScript, each synchronization task can not take too long, or they will not let the program accordingly, React update process is guilty of this taboo to other inputs, while React Fiber is to change the status quo.

React Fiber way

Ways to break JavaScript in synchronous operation for too long is very simple - fragmentation.

Put a time-consuming task into many small pieces, every little piece of the running time is very short, although the total is still a very long time, but after each small piece executed, gave a chance to perform other tasks, so that not only thread will be occupied by other tasks still have a chance to run.

React Fiber fragmentation of the update process, the implementation process as shown in the following diagram, each completes the execution of the update process, took control to the module back to React responsible for the coordination of tasks, see if there are other urgent tasks to be done, if not continue to update, if there is an emergency task, then do it urgent task.

Maintain the data structure of each fragment is Fiber.

Once you have fragmented, call stack update process as shown below, the middle of each representative of a deep trough during the execution of a fragment, a piece of each peak is the end of the implementation of the return of control over the timing points.

Specifically to see the article below React Fiber

React Fiber can be seen in the use of asynchronous rendering of the life cycle of the components had an impact, since every component update is no longer before the entire process in accordance with updated simultaneously down, but is divided into two parts. Prior to render and render after

Prior to render
  • constructor()
  • componentWillMount()
  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
After render
  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

Components render () after the interface has been rendered, it is not affected, mainly affected before the render () function of the life cycle, we have to look at the specific render several functions before ().

constructor()

Constructors components throughout the life cycle called only once, so unaffected.

componentWillMount()

When the initial loading component, called render () before, after use Fiber, may not continue to render () function after execution, has been called the next time slice, it may be time to render the case of multiple calls. coumponentWillReceiveProps () componentWillUpdate () empathy.

componentWillUpdate()

For optimizing the performance of the components, the function returns true and false. Because the function is only used to determine whether to continue render () function, to render () eventually executed, or because of multiple calls to asynchronous reason Fiber has no effect.

Therefore, the main affected is componentWillMount () coumponentWillReceiveProps () componentWillUpdate () these three functions, React official is ready to remove the life cycle of these three functions in a subsequent version 17, the official but also out of two new life-cycle function used to replace the missing functionality. These two functions is:

  • getDeriverdStateFromProps()
  • getSnapshotBeforeUpdate()

After react 16.3, the life cycle diagram becomes so

We look at the life cycle of these two new functions:

getDerivedStateFromProps is a static function, the function body can not access this, simply put, is to be a pure function, pure function is a good thing ah, output is completely determined by the input.

static getDerivedStateFromProps(nextProps, prevState) {
    // 这一生命周期方法是静态的,它在组件实例化或接收到新的 props 时被触发
    // 通过 nextProps, prevState 进行数据处理,如需更新组件state则返回一个对象,
    // 则将被用于更新 state ;如不需更新则返回一个 null ,则不触发 state 的更新

    // 配合 `componentDidUpdate` 使用,这一方法可以取代 `componentWillReceiveProps`
  }

复制代码

getSnapshotBeforeUpdate (prevProps, prevState) can be seen from the graph of this function is called after the render, Logically, for us after this operation can be written directly componentDidUpdate inside, and later carefully to understand it and found that life cycle function is a real and browser render rendered intermediate, FIG follows:

How to use this function, in fact, I could not find a suitable example to explain, we can look at the example being given official:

class ScrollingList extends React.Component {
  listRef = null;

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the scroll position so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      return (
        this.listRef.scrollHeight - this.listRef.scrollTop
      );
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // If we have a snapshot value, we've just added new items.
    // Adjust scroll so these new items don't push the old ones out of view.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      this.listRef.scrollTop =
        this.listRef.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.setListRef}>
        {/* ...contents... */}
      </div>
    );
  }

  setListRef = ref => {
    this.listRef = ref;
  };
}
复制代码

In this example, the use of getSnapshotBeforeUpdate, just adjust the scroll position in the list after adding a new line in the list.

React Hook



React two forms assembly

  • Class (stateful) component
  • Function (stateless) component

When a component is not complicated, we usually functional components to write, but often as the business changes, the internal components of the need for the state to maintain, so often again Stateless components have changed state assembly. Can not let the functional components can have state? This time appeared Hook, Hook but far more than that, according to the official React opinion, I hope you try to use functional components to replace the Class components, so we should be able to assume the role of Hook Class component lifecycle functions.

Why introduction Hooks?

The official motive react given is used to solve some difficult problems to avoid prolonged use and maintenance react encountered. such as:

  1. Difficult to reuse and sharing components associated with the logic state
  2. Logic complex components difficult to develop and maintain, the assemblies when we need to deal with multiple local state unrelated, the life cycle of each function may contain a variety of unrelated logic in it.
  3. Class component of this increase learning costs, there is little question in class components based on the optimization of existing tools.
  4. As the business changes, function components had to be changed class components and so on.

Prior to further understanding, we need to quickly understand some basic Hooks usage.

One of the easiest Hooks

First, let's look at a simple stateful components:

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

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

Let us look at using a version of hooks:

import { useState } from 'react';

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

 return (
  <div>
   <p>You clicked {count} times</p>
   <button onClick={() => setCount(count + 1)}>
    Click me
   </button>
  </div>
 );
}
复制代码

It is not it much simpler! You can see, Example becomes a function, but this function have their own state (count), but it also can update their status (setCount). The reason why such a function should not, because it has injected a hook - useState, this hook is to make our function becomes a function of the state.

It can be seen to strengthen the ability to hook completely functional components, while not increasing the functional components of more complex, it becomes more powerful. Hooks goal is to let us appear to use more of the functional components. We can observe the results compiled in two way babel in.

Cited above is just a simple usage, hook the ability to much more than that.

What is the State Hooks?

Back to the beginning of the example we use, we decompose look at what has been done in the end state hooks:

import { useState } from 'react';

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

 return (
  <div>
   <p>You clicked {count} times</p>
   <button onClick={() => setCount(count + 1)}>
    Click me
   </button>
  </div>
 );
}
复制代码

Declare a state variable

import { useState } from 'react';

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

复制代码

useState is react comes with a hook function, its role is to declare a state variable. useState function receives the parameter is our state an initial value (initial state), it returns an array of [0] the array of the current the current state value of [1] is a way to change the status value function.

Therefore, we do in fact, declared a state variable count, put its initial value is set to 0, while providing a count can change the function of setCount.

update status

 <button onClick={() => setCount(count + 1)}>
  Click me
 </button>
复制代码

When the user clicks the button, we call setCount function that received parameter is modified the new status value. The next thing to react to the, react will re-render our Example components, and are using the new updated status, that is, count = 1. Here we have to stop and think about the nature of the Example is a normal function, why the state before it can remember?

A critical issue

Here we discovered the problem, generally speaking we declare a variable in a function, when the function is complete, this variable also destroyed (not considered here we closures, etc.), for example, consider the following example:

function add(n) {
  const result = 0;
  return result + 1;
}

add(1); //1
add(1); //1
复制代码

Whether we repeatedly call the add function of how many times, the result is 1. Because every time we call add, result variables are from an initial value of zero. Why, when the above Example function of each execution, are taking on the state values ​​at once performed as the initial value? The answer is: Yes react to help us remember. As to react with what mechanism should be remembered, then we can think about.

If a component has multiple state values ​​how to do?

First, useState can be called multiple times, so we can write:

function ExampleWithManyStates() {
 const [age, setAge] = useState(42);
 const [fruit, setFruit] = useState('banana');
 const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
复制代码

Secondly, the initial value is not received useState must be a predetermined string / number / boolean such a simple data type, which can receive objects or arrays as parameters. The only point to note is that, before we do is return the this.setState a new state after the merger state, and returns the new state useState is a direct replacement for the old state. Finally, react also provides us with a useReducer the hook, if you prefer the style of state management program redux words.

We can see from ExampleWithManyStates function, useState no matter how many times the calls are independent of each other. This is critical. Why do you say?

In fact, we see the hook of "form", somewhat similar to before officially dismissed out of Mixins this program, is providing a "plug-in function of injecting" capability. The mixins reason was negative, because Mixins mechanism allows multiple Mixins share data space of an object, making it difficult to ensure that different Mixins dependent on the state of conflict does not occur.

And now we have the hook, on the one hand it is used directly in the function which, instead of class; on the other hand are independent of each hook, the different components of a hook with calls but also to ensure the independence of their state. This is the essence of the difference between the two.

react is how to ensure plurality of mutually independent useState of?

ExampleWithManyStates still see the example given above, we called three times useState, but every time we pass a parameter value (such as 42, 'banana'), we did not react to tell these values ​​corresponding key is which, and that is how react the three useState ensure that it corresponds to the state to find it?

The answer is, react according to the order useState appear to set. Specifically, we look at

//第一次渲染
 useState(42); //将age初始化为42
 useState('banana'); //将fruit初始化为banana
 useState([{ text: 'Learn Hooks' }]); //...

 //第二次渲染
 useState(42); //读取状态变量age的值(这时候传的参数42直接被忽略)
 useState('banana'); //读取状态变量fruit的值(这时候传的参数banana直接被忽略)
 useState([{ text: 'Learn Hooks' }]); //...

复制代码

If we change the code below:

let showFruit = true;
function ExampleWithManyStates() {
 const [age, setAge] = useState(42);
 
 if(showFruit) {
  const [fruit, setFruit] = useState('banana');
  showFruit = false;
 }
 
 const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
复制代码

Thus,

 //第一次渲染
 useState(42); //将age初始化为42
 useState('banana'); //将fruit初始化为banana
 useState([{ text: 'Learn Hooks' }]); //...

 //第二次渲染
 useState(42); //读取状态变量age的值(这时候传的参数42直接被忽略)
 // useState('banana'); 
 useState([{ text: 'Learn Hooks' }]); //读取到的却是状态变量fruit的值,导致报错
复制代码

In view of this, react regulations we have to write hooks in the outermost layer of the function, you can not write in ifelse such as conditional statements which, to ensure consistent execution of the order hooks.

What is the Effect Hooks?

We've added a new feature on an example:

import { useState, useEffect } from 'react';

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

 // 类似于componentDidMount 和 componentDidUpdate:
 useEffect(() => {
  // 更新文档的标题
  document.title = `You clicked ${count} times`;
 });

 return (
  <div>
   <p>You clicked {count} times</p>
   <button onClick={() => setCount(count + 1)}>
    Click me
   </button>
  </div>
 );
}
复制代码

We compare the look, without hooks, how will we write?

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

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

 componentDidUpdate() {
  document.title = `You clicked ${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>
  );
 }
}

复制代码

We write stateful components, usually produces a lot of side effects (side effect), such as initiating ajax request to obtain data, add some of monitor registration and cancellation of registration, manually modify the dom and so on. Before we regard the function of these side effects in the life cycle of a write function hooks in such componentDidMount, componentDidUpdate and componentWillUnmount. And now useEffect hook quite a collection of these statements and periodic function. It arrived with a three.

At the same time, due to the previously mentioned hooks can be used repeatedly, independent of each other. So our reasonable approach is to give each side a separate useEffect hook. As a result, these side effects will not fall into the heap in the life cycle of the hook, the code becomes clearer.

useEffect did what?

We then comb it again the logic of the following code:

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

 useEffect(() => {
  document.title = `You clicked ${count} times`;
 });

复制代码

First, we declare a state variable count, to its initial value is set to 0. Then we tell react, we have a side effect of this component. We give useEffecthook pass an anonymous function, this is our anonymous function side effects. In this example, we call side effects browser API to modify the document title. When we react to render the assembly, it will first remember the side effects we use. Etc. After react updated DOM, which in turn perform the function of the side effects we've defined.

Here points to note:

First, react after the first rendering and render each function will be called again to pass the useEffect. And before we have to use two lifecycle functions to represent the first rendering (componentDidMount), re-render (componentDidUpdate) and after the update caused.

Second, the implementation of the side effects of useEffect defined function does not prevent the browser to update the view, that these functions are executed asynchronously, whereas the previous componentDidMount or componentDidUpdate code is executed synchronously. This arrangement is reasonable to say that most of the side effects, except in some cases, such as we sometimes need to re-render calculated according to the size of a DOM element, this time we hope that the re-rendering is simultaneously generated , which means that it will happen before the browser is really to draw this page.

useEffect how unbundling some side effects

This scenario is very common, when we add a registered componentDidMount, we have to immediately componentWillUnmount, that is cleared before the component being written off registration we add, otherwise a memory leak problem arises.

How to remove it? Let us pass useEffect side effects function returns a new function can be. After the implementation of this new function will once again render in components. This pattern is common in implementing some pubsub mode. See the examples below:

import { useState, useEffect } from 'react';

function FriendStatus(props) {
 const [isOnline, setIsOnline] = useState(null);

 function handleStatusChange(status) {
  setIsOnline(status.isOnline);
 }

 useEffect(() => {
  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  // 一定注意下这个顺序:告诉react在下次重新渲染组件之后,同时是下次调用ChatAPI.subscribeToFriendStatus之前执行cleanup
  return function cleanup() {
   ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
 });

 if (isOnline === null) {
  return 'Loading...';
 }
 return isOnline ? 'Online' : 'Offline';
}

复制代码

There is a need to focus on point! This unbundling model is not the same with componentWillUnmount. componentWillUnmount will be destroyed before the assembly performed once only, and useEffect in function after each component rendering will be executed again, including the side effects of this function returns the cleanup function will be re-run again. So we look together the following problem.

Why should the side effects are a function of each component updates perform again?

We look at the previous model:

 componentDidMount() {
  ChatAPI.subscribeToFriendStatus(
   this.props.friend.id,
   this.handleStatusChange
  );
 }

 componentWillUnmount() {
  ChatAPI.unsubscribeFromFriendStatus(
   this.props.friend.id,
   this.handleStatusChange
  );
 }

复制代码

Very clear, we are registered in componentDidMount, and then clear the registration componentWillUnmount. But if this time props.friend.id changed how do? We have no choice but to add a componentDidUpdate to handle this situation:

componentDidUpdate(prevProps) {
  // 先把上一个friend.id解绑
  ChatAPI.unsubscribeFromFriendStatus(
   prevProps.friend.id,
   this.handleStatusChange
  );
  // 再重新注册新但friend.id
  ChatAPI.subscribeToFriendStatus(
   this.props.friend.id,
   this.handleStatusChange
  );
 }
复制代码

see it? Very complicated, but we useEffect is not the problem, because it will be re-run again after each component updates. Therefore, the execution order of the code is such that:

  1. First page rendering
  2. Friends for friend.id = 1 registration
  3. Suddenly turned into a 2 friend.id
  4. Page re-rendering
  5. Clear binding friend.id = 1
  6. For registered friend.id = 2 friends

How to skip some unnecessary side effects function

Follow the instructions on a way of thinking, each time re-rendering functions to be executed again these side effects, obviously uneconomical. How to skip some unnecessary calculate it? We just need to give useEffect pass the second parameter. By the second argument to tell react only when the value of this parameter is changed, we pass before the implementation of the side effects of function (first parameter)

useEffect(() => {
 document.title = `You clicked ${count} times`;
}, [count]); // 只有当count的值发生变化时,才会重新执行`document.title`这一句
复制代码

When we pass a second parameter empty array [], in fact, it is equivalent to only performed for the first time when rendering. Effect mention that this current does not depend on other variable parameters. More questions about the dependencies can view the official document, the document has a very detailed explanation. If my dependent effect of frequent changes, how can I do? And how to deal with functions

More information about Hook consult the official documentation Hook Profile

Guess you like

Origin blog.csdn.net/weixin_34329187/article/details/91399661