A little contradiction between React Supense and React asynchronous rendering

记得要微笑
中文

React's Suspense function simply means that when the component rendering encounters an asynchronous operation, it can seamlessly "suspense" it. When the asynchronous operation has a result, it can continue seamlessly.

The asynchronous operations mentioned here can be divided into two categories:

  • Load code asynchronously
  • Load data asynchronously

It's easy to recognize and write programs. The toss is nothing more than "code" and "data".

load code asynchronously?

Originally, the code is packaged into one file, but when the amount of code is huge, and not all the code is used when the page is loaded, pulling them into the only packaged file, there is no benefit in addition to the simple packaging process. Therefore, in order to squeeze performance, it is necessary to consider packaging the code into several files, so that each packaged file can be relatively small and loaded as needed. This is a good idea, but it’s awkward for every developer to implement this mechanism. Therefore, React uses Suspense to provide a unified and seamless code splitting and asynchronous loading method, in v16.6.0 This kind of Suspense function is realized.

Everyone is interested in playing on your own. This kind of Suspense is not the focus of today. Today I will talk about the "asynchronous loading of data" Suspense, which is to use Suspense to call server APIs and other operations.

According to React's official roadmap, using Suspense for data loading will not be released until the middle of this year (2019). If you see this article late, it may have already been released.

today is that Suspense does data loading, and there is a little contradiction with React v16's main event, asynchronous rendering, .

In the previous Live "In-depth understanding of the new features of React v16" I said that starting from React v16, the life cycle of a component can be divided into two phases: render phase + commit phase. The life cycle function in the render phase may be interrupted due to the design characteristics of the Fiber. After being interrupted, it will be called again; and once the commit phase starts, it will never be interrupted. The dividing line between the render phase and the commit phase is the render function. Note that the render function itself belongs to the render phase.

For example, a component is rendered and executed in the render function. At this time, the user suddenly enters something in an input control. At this time, React decides to give priority to the key events in the input control, and it will interrupt the component. The rendering process, that is, no matter what the render returns, the rendering process will stop there, no drawing, and focus on processing the input controls. Wait until the things over there are processed, and then render this component, but this time to restart from the original position, it must be unreliable, because the key event processing just now may have changed some states, in order to ensure absolute reliability, React decided ...... Let's go over from the beginning, so, re-tune getDerivedStateFromProps, shouldComponentUpdate and then call render.

Seeing it, the life cycle functions before render will be called, and because this "interruption" is completely unpredictable, now requires all life cycle functions in the render phase not to have side effects Operation .

What are side effects? It is the operation that a pure function should not do.

What is a pure function? That is, in addition to returning the result according to the input parameters, it does not do any multi-task operations.

If a function modifies global variables, it is not a pure function; if a function modifies the state of a class instance, it is not a pure function; if a function throws an exception, it is not a pure function; if a function accesses the server through AJAX API, it is not a pure function.

Take access to the server API as an example. If the life cycle function of the render phase performs an AJAX operation to access the server API, then it is very likely that continuous access to the server will occur, because the render phase will be interrupted and executed repeatedly under asynchronous rendering. .

class Foo extends React.Component {
  shouldComoponentUpdate() {
    callAPI(); // 你只看到了一行代码,但是可能会被多次调用
    return true;
  }

  render() {
    callAPI(); // 你只看到了一行代码,但是可能会被多次调用

    return JSX;
  }
}

Once again, all life cycle functions in the render phase should not perform side-effect operations. These functions must be pure functions.

So, now the question is, will you use Suspense to get data, will you violate this rule?

Although Suspense's API in this area has not yet been determined, the code form is still clear. Use the demo version of react-cache to show it.

import React, { Suspense } from "react";
import { unstable_createResource as createResource } from "react-cache";

// 模拟一个AJAX调用
const mockApi = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("Hello"), 1000);
  });
};

const resource = createResource(mockApi);

const Greeting = () => {
  // 注意看这里,这里依然是在render阶段,可能会被重复调用哦!
  const result = resource.read();

  return <div>{result} world</div>;
};

const SuspenseDemo = () => {
  return (
    <Suspense fallback={<div>loading...</div>}>
      <Greeting />
    </Suspense>
  );
};

export default SuspenseDemo;

It is interesting here. Use Suspense to get data. Since the data is used in the render function (or in the function type component like the above example), the process of obtaining data must be in the render phase, but the process of obtaining data is to Those who call AJAX, AJAX is a side-effect operation. Isn't this contradictory to the rule that "side-effect operations cannot be performed in the render phase"?

It is indeed a bit contradictory.

The Reac developers explained this as follows:

image.png

Simply put, it reduces the requirements, and only needs the operation of the render stage to be "indempotent".

The so-called idempotence means that one call and N calls produce the same result.

Let's give an example.

// 这是纯函数,没有副作用
function foo1(a, b) {
  return a + b;
}

// 这不是纯函数,有副作用,而且不幂等
function foo2(a, b) {
  sendAjax(a + b);
  return a + b;
}

// 这不是纯函数,有副作用,但是——幂等
let called = false;
function foo3(a, b) {
  if (!called) {
    sendAjax(a + b);
  }
  called = true;
  return a + b;
}

The above function foo3 does have side effects. However, the code is used cleverly to prevent the first call to issue AJAX, and subsequent calls will not issue AJAX. In this way, how many times it is called, the effect will be the same. It is "idempotent".

Although idempotence is not as pure as a pure function, it is good enough, at least for a framework that cannot be "pure" like React, this is also the best result.

The unstable_createResource function of the demo version of react-cache accepts a function that returns a Promise as a parameter. The obtained Promise will be cached, so although it will be interrupted and repeated many times, if resource.read() returns a result, Then the returned results are the same, and the "idempotent" effect is achieved.

In this way, the contradiction is resolved.

To sum up, in fact, we have to " in the render phase of all life cycle functions do not have side-effect operations, these functions must be pure functions "change this requirement, change it to" all life cycle functions in the render phase All should be idempotent ".

Although React often talks about "functional programming", React really has a lot of distance from the "functional programming" that advocates pure functions, including Hooks. On the surface, functional components are admired, and it seems to be function-oriented. Programming is a step forward, but all Hooks functions are stateful, so how can they be considered pure functions?

Of course, there is no need to pursue pure functions like cleanliness. Since "idempotence" can solve the problem, we are also happy to see it.

It confirms the old saying: As long as you lower the standard, you will find that the world suddenly becomes brighter :)

PS If the background knowledge in this article is not clear, just go to my previous Live. I have said all the knowledge points.

"Quickly understand the new features of React Suspense and Hooks"

"In-depth understanding of the new features of React v16"

"Help you understand React in depth"

阅读 539

avatar
记得要微笑
前端工程师

求上而得中,求中而得下,求下而不得

1.1k 声望
1.9k 粉丝
0 条评论
你知道吗?

avatar
记得要微笑
前端工程师

求上而得中,求中而得下,求下而不得

1.1k 声望
1.9k 粉丝
文章目录
宣传栏