React 01 - Source Code, Mechanism of Suspense and unstable_createResource

Brian Shen
4 min readMay 18, 2020

--

Everyone seems very excited about this feature. Even in interviews, they would like to ask whether you have tried this feature, whether you know about the mechanism. While most people use them in daily development, few knows how is it implemented. So today, we will analyze the source code and dive deep in this feature.

Mainly, we will discuss 3 parts:

  • i. Suspense Use Cases
  • ii. Source Code and Mechanism
  • iii. Data Fetching and unstable_createResource?

i. Suspense Use Cases

If we look at the official document of React, we can see Suspense mainly can be used in 2 places:

For code split, it is quite simple. Using React.lazy with Suspense, we can quickly split our code, without too much code refactor.

You can see the results in Code Split with Webpack and Suspense in React .

For data fetching, we will talk about it later.

ii. Source Code and Mechanism

So why this simple change ( lazy and Suspense ) makes code split happen? Let’s see the official explanation of Suspense:

  1. In the render method, read a value from the cache
  2. If the value is already cached, the render continues like normal
  3. If the value is not already cached, the cache throw a promise
  4. When the promise resolves, React retries where it left off

Quite simple, right? So we can draw a conclusion:

  • lazy is some kind of a cache, it turns a normal import component into a cacheable component ( lazyComponent )
  • Suspense implements reading from the cache, wait and retries where it left off

With this in mind, let’s see the source code.

1. read a value from the cache

ReactFiberBeginWork.js :

let Component = readLazyComponentType(elementType);

2 && 3. If the value is already cached, the render continues like normal && If the value is not already cached, the cache throw a promise

ReactFiberLazyComponent.js

The throw promise is to fetch the cache content. Then how to fetch them? Use ctor:

ReactLazyComponent.js

When is ctor passed to lazyComponent?

ReactLazy.js

When we use lazy to import a component, the import itself is a Thenable class.

So where is 4. When the promise resolves, React retries where it left off?

Let’s implement one! Since lazyComponent will throw error if no cache found, so we catch them (in componentDidCatch) , wait them to resolve and retry:

And use it to replace Suspense:

Run it:

cd webpack_Learn
npm i
npm run-script start_server
npm start

Wow, it works~

iii. Data Fetching and unstable_createResource

In i Suspense Use Cases, we talked about 2 aspects:

  1. Code Split
  2. Data Fetching

But we don’t give explanation or examples of data fetching. Why? Because it is complicated, and even React itself are experimenting them. It brings revolution to the relationship between fetch and render.

  • Fetch-on-render (for example, fetch in useEffect): Start rendering components. Each of these components may trigger data fetching in their effects and lifecycle methods. This approach often leads to “waterfalls”.
  • Fetch-then-render (for example, Relay without Suspense): Start fetching all the data for the next screen as early as possible. When the data is ready, render the new screen. We can’t do anything until the data arrives.
  • Render-as-you-fetch (for example, Relay with Suspense): Start fetching all the required data for the next screen as early as possible, and start rendering the new screen immediately — before we get a network response. As data streams in, React retries rendering components that still need data until they’re all ready.

With suspense, now we Render-as-you-fetch. In React Official Document and other blogs, they mainly use react-cache for demos. But react-cache is unstable, as we can see form the method name — unstable_createResource and it is incompatible with latest React. However, we shall still see the source code and try to implement a simple one ourselves!

The mainly usage is below.

And the source code ReactCache.js :

As we read from resource, if not cached, throw a promise, and Suspense then can catch it, wait it and retry.

OK, since react-cache is not compatible with latest React , we create a simple one! Keep these 2 key points in mind as we had saw in previous section:

  1. If the value is already cached, the render continues like normal (return the cache resource)
  2. If the value is not already cached, the cache throw a promise (throw a promise)

Try to use them:

cd webpack_Learn
npm i
npm run-script start_server
npm start

Yeah! We did it! And as we can see, all resources are loaded only once!

What is this place? Dali Ancient City, Dali, Yunnan, China

--

--