Takuya Kikuchi
TK's Blog ✍️

TK's Blog ✍️

A high-level overview of Concurrent React

Takuya Kikuchi's photo
Takuya Kikuchi
·Jul 23, 2022·

5 min read

Play this article

Click here to read the article in Japanese: zenn.dev/takuyakikuchi/articles/91ccf7037d6..

About this Article

Concurrent React is more important than a typical implementation detail — it’s a foundational update to React’s core rendering model. So while it’s not super important to know how concurrency works, it may be worth knowing what it is at a high level.
quoted from React v18.0 – React Blog

Here's an article to get a high-level overview of Concurrent React, an essential change to React's core rendering model.

Concurrent React is not a feature, but a new mechanism

Concurrency is not a feature, per se. It’s a new behind-the-scenes mechanism that enables React to prepare multiple versions of your UI at the same time.
quoted from React v18.0 – React Blog

First of all, not a feature, as quoted above, Concurrent React is a new mechanism (React's new core rendering model) to allow React to have multiple versions of the UI ready at the same time.

What is new is the following

  • Before: rendering is collective, uninterrupted, and synchronous
  • Concurrent: rendering is interruptible and asynchronous (a key characteristic of Concurrent React)

With this new mechanism,
Users get a smoother user experience,
And, developers will be able to more declaratively describe the control of the UI based on the loading state of components.

To understand more concretely what this means, let's talk about one of Concurrent React's features, Suspense.

Suspense

Here is a conceptual explanation of Suspense with a simple code example.
(Code taken from React Top-Level API – React)

<Suspense fallback={<Spinner />}>
  <Comments />
</Suspense>

Suspense handles the situation where an internal component is loading but not yet ready to render.

In the previous section, I said that rendering is interruptible and asynchronous, which is an important characteristic of Concurrent React.
In this example,

  1. rendering <Comments> is suspended while it is loading(interruptible)
  2. <Spinner> is displayed as a fallback UI while <Comments> is loading
  3. when <Commnets> completed loading, it is rendered(Asynchronous rendering)

This way, the UI is displayed consistently thus UX is smooth.

Here is another example.

function ProfilePage() {
  return (
    <PageLayout>
      <Suspense fallback={<MyProfileSkeleton />}>
        <MyProfile />
      </Suspense>
      <Suspense fallback={<AchievementsSkeleton />}>
        <Achievements />
      </Suspense>
      <Suspense fallback={<OrganizationSkeleton />}>
        <Organizations />
      </Suspense>
      <Suspense fallback={<ContributionsSkeleton />}>
        <Contributions />
      </Suspense>
    </PageLayout>
  );
};

In this example, each is enclosed in <Suspense>.
This allows them to have independent skeleton views and to be displayed asynchronously from the point where loading is complete. Thus, by changing the way Suspense is placed, asynchronous UI rendering can be finely controlled.

Suspense Use Cases

Now, let's look at a concrete use case of Suspense.

Lazy loading of components using React.lazy

The only use case at this time. (See: React Top-Level API – React)

// This component is loaded dynamically
const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    // Displays <Spinner> until OtherComponent loads
    <React.Suspense fallback={<Spinner />}>
      <div>
        <OtherComponent />
      </div>
    </React.Suspense>
  );
}

This is supported since React v.16.6, so you may already be familiar with this use case.

The above example displays content <Spinner> for fallback while waiting for the loading of a lazy component <OtherComponent> using React.lazy.

React.lazy is a function used to perform code splitting, and rendering the lazy component within a Suspense component ensures that the UI is displayed consistently. Code-Splitting – React

Suspense with data fetching

The only officially supported use case at this time is using React.lazy, but the vision the React team has for Suspense seems to be much larger.

As in previous versions of React, you can also use Suspense for code splitting on the client with React.lazy. But our vision for Suspense has always been about much more than loading code — the goal is to extend support for Suspense so that eventually, the same declarative Suspense fallback can handle any asynchronous operation (loading code, data, images, etc).
Quoted from React v18.0 - React Blog

One of these is the use of Suspense in data fetching.

Conventional way

[※Alert: The following is a code example using the Apollo Client notation, but the Apollo Client does not support Suspense at this time.
Github issue about Suspense support: github.com/apollographql/apollo-client/issu..

// Dog.jsx
function Dogs() {
  const { loading, error, data } = useQuery(GET_DOGS);

  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;

  return (
    <ul>
      {data.dogs.map((dog) => (
        <li key={dog.id}>{dog.breed}</li>
      ))}
    </ul>
  );
}

In components that perform asynchronous loading, the "process during loading" and the "process when loading is completed" are combined.

Suspenseful way

// Dogs.jsx
function Dogs() {
  const { data } = useQuery(GET_DOGS);

  return (
    <ul>
      {data.dogs.map((dog) => (
        <li key={dog.id}>{dog.breed}</li>
      ))}
    </ul>
  );
};
// App.jsx
function App() {
  return (
    <React.Suspense fallback={<Spinner />}>
      <Dogs />
    </React.Suspense>
  );
};

Whereas the previous program was procedural, such as if (isLoading), the processing of the loading state has been made more declarative. This simplifies the responsibilities of the component responsible for loading data.

The above is just an idea as a code example, but if you want to start using it in practice, you can start using Suspense for data fetching in React 18 by using frameworks such as Relay, Next.js, Hydrogen, and Remix. (*Not yet recommended as a general strategy in the sense that it is technically possible.)
In the future, they may provide new basic functionality to easily access data with Suspense without using a framework, so we look forward to future updates.
See Suspense in Data Framework

Other Use Cases

The following are other use cases, which are only referenced links.
Server-side components + Suspense is a feature that I am quite personally excited about.

Summary

Concurrent React is not only a better user experience, but as a developer, I felt that we need to design for the features that will be available with Concurrent React.
I am sure that both new features by Concurrent React and Concurrent support for the React ecosystem will be updated more and more in the future, so keep an eye on Concurrent React in the future.

Reference

 
Share this