Many people may not have used Suspense in their projects, but Suspense is a very important part of React’s future development.

This article will explain the significance of Suspense for React.

React’s iterative process

React has gone through three major changes in its main features from v16 to v18.

  • v16: Asynchronous Mode
  • v17: Concurrent Mode
  • v18: Concurrent Render (concurrent update)

To understand the significance of these three changes, you need to first understand a confusing concept in React – render.

The render function of a ClassComponent is called render when it is executed.

1
2
3
4
5
class App extends Component {
  render() {
    // ...这是render函数
  }
}

And the process of rendering the result of render to the page is called commit.

The purpose of Async Mode is to make rendering asynchronous and interruptible.

The purpose of Concurrent Mode is to make commit concurrent in the user’s perception.

Since Concurrent Mode includes breaking change, v18 proposes Concurrent Render to reduce the cost of migration for developers.

So what does it mean to make commits perceptually concurrent for users?

The meaning of “concurrency”

When it comes to concurrency, we have to mention Suspense. consider the following code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const App = () => {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    setInterval(() => {
      setCount(count => count + 1);
    }, 1000);
  }, []);
  
  return (
    <>
      <Suspense fallback={<div>loading...</div>}>
        <Sub count={count} />
      </Suspense>
      <div>count is {count}</div>
    </>
  );
};

Of which.

  • An update will be triggered every second, updating the state count to count => count + 1
  • An asynchronous request will be initiated in the Sub, and the Suspense wrapping the Sub will render the fallback before the request returns

Assuming the request returns in three seconds, ideally the page before and after the request is initiated will be displayed in order as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Sub内请求发起前
<div class="sub">I am sub, count is 0</div>
<div>count is 0</div>

// Sub内请求发起第1秒
<div>loading...</div>
<div>count is 1</div>

// Sub内请求发起第2秒
<div>loading...</div>
<div>count is 2</div>

// Sub内请求发起第3秒
<div>loading...</div>
<div>count is 3</div>

// Sub内请求成功后
<div class="sub">I am sub, request success, count is 4</div>
<div>count is 4</div>

From the user’s perspective, there are two tasks in the page that are executing concurrently.

  • the task of requesting a Sub (observing the change of the first div)
  • the task of changing count (observing the change of the second div)

Suspense brings the feeling of multi-task concurrent execution in a page, which is what Concurrent means in React.

Suspense is actually supported in Async Mode, but the above code behaves as follows in an Async Mode page.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Sub内请求发起前
<div class="sub">I am sub, count is 0</div>
<div>count is 0</div>

// Sub内请求发起第1秒
<div>loading...</div>
<div>count is 1</div>

// Sub内请求发起第2秒
<div>loading...</div>
<div>count is 2</div>

// Sub内请求发起第3秒
<div>loading...</div>
<div>count is 3</div>

// Sub内请求成功后
<div class="sub">I am sub, request success, count is 4</div>
<div>count is 4</div>

From the user’s point of view, when the task that requested Sub is executed, the task that changed count is frozen.

This is why it is called Async and not Concurrent.

The significance of Suspense

As you can see, for Concurrent, Suspense is an essential piece of the puzzle.

It can be considered that the role of Suspense is to divide the parts of the page that need to be rendered concurrently.

For example, in the above example, the task of requesting Sub is separated from the task of changing count by Suspense, which is visually executed concurrently.

Once the meaning of Suspense is clear, you’ll see that the next thing React is doing is to keep expanding the scenarios for Suspense (i.e. to include more scenarios for concurrent rendering).

For example, the currently existing.

  • React.lazy
  • Asynchronous requests transformed by the fetch library provided by React
  • useTransition
  • useDeferredvalue

To be added in the future.

  • Server Component
  • Selective Hydration

Summary

React has evolved from synchronous to asynchronous and then to concurrent.

Once concurrency is implemented, the next step in the evolution will be to keep extending the scenarios where concurrency can be used.

The role of Suspense is to divide the parts of the page that need to be rendered concurrently.

This path has been decided since the beginning of React, because architecturally, React relies heavily on the runtime, and concurrency is the optimal direction for this architecture in order to optimize performance.