React Gets First Class Support for Async/Await – Yes or Meh? , by Jose Granza | October, 2022

A look at the React RFC draft proposal support for loading data

React gets first class support for Async/Await - use hooks
caption by author

The first cornerstone of React’s first-class support for async/await was when the team started <Suspense> on feedback 16.6, At that time, there was still no concurrent mode and its functionality was limited. The suspended component will render and be hidden only on the DOM. it was known as Legacy Suspense,

Suspense There was to be an important mechanism on which all other APIs would depend.

When concurrent mode landed, Suspense The API was extended:

  • The execution of the react component has been stopped. Element until mounted ComponentThatSuspends has been resolved.
  • Effects/Lifecycles are run properly.

To date, the lazy loading component is the only use case that is supported. <Suspense>, The React team has been figuring out how to combine data loading for a long time Suspense, In this published RFC draft, we can finally see how they plan to build it.

The approach that the React team is taking is quite traditional, but with a twist.

They have chosen to implement this feature in two flavors:

  • React Client: a introducing by introducing use hook
  • React Server Components: Native Support async/await Es7 syntax.

use hook

This hook is as controversial as its name. They have chosen this particular name in an attempt to set it apart from the rest. Why? Because this hook is different: it can run conditionally.

This alone breaks down the whole concept of hooks that we’ve learned over the years. It is quite remarkable how the React team put that idea into our mind. There was even a famous post by Dan Abramov explaining why in detail (link here).

how can use Can hooks be applied conditionally? Because simplified, it’s just a . throws Promise who is caught by the nearest Suspense Parents

What is the caveat of this approach? Different async/await, the react component will not resume where it left off. Instead, the suspended component will be re-rendered. This is a side-effect of the React components being inactive, how ironic.

Let’s look at the code example:

The above code seems simple and easy to reason about. There is only one catch. <Test /> The component will re-render endlessly.

Why? As seen earlier, the render is stopped by throwing a promise-like exception. The entire component will be re-rendered when the promise is resolved. it means that fetchData The method will be invoked again resulting in another re-render and so on.

What are the possible improvements?

  • Create a cache mechanism for resolved promises.
  • wait and use cache API that the React team is working on. it would be something like use(cache(fetchData)) Adding more complexity to the mix.

cache The API hasn’t seen a draft yet. The feature most likely to land use The feature has been shipped.

Async/Await on Server Components

The way we deal with fetching data in server components is a bit different. The React team claims that this can have a positive effect as it forces developers to be more careful. what is it Good for? Because server components have some restrictions that always have to be taken into account.

Server components will be able to use the standard async/await syntax. Here, they are bending the server component’s rule not to reach hooks. they’ve already turned it once useId hook.

Let’s look at an example:

React code example removed from RFC proposal

The above sounds more natural and easier to reason about. Why doesn’t React Client use that solution? There are some shortcomings which need to be addressed. In short, how to do it? async The method works and they will return a new promise on each execution.

This can cause the UI to be suspended due to an arbitrary property. More details here.

React has three release channels:

  • latest:for stable,React release. This is the channel to use for all production feedback applications.
  • Next: You can think of it as a release candidate channel. Third-party projects mostly use it.
  • Experimental: For APIs and features that are not available in the stable release. This is for trying out new releases as they are released. You can think of it as an early preview.

nOte: Only latest Channels uses semantic versioning. next And experimental The hashes of their contents and are tagged by the commit date.

So we can go ahead and install the latest experimental Release for a quick preview of use hook. Please note that this special release experimental The change is likely to differ from the final response. So expect breaking changes and it should never be used in a user facing application.

It is easy to install:

yarn upgrade react@experimental react-dom@experimental

When that is done you can simply start using the new Draft API

import {experimental_use as use} from 'react';

I understand that the React team is geared towards designing interfaces where we can skip the loading spinner hell.

This is achieved by blending fetch data with all existing Suspense API like useTransition , useDeferred … the user would really benefit from interfaces that don’t constantly display irrelevant loading status everywhere. Their browser would also benefit from not executing component code that would get trashed after a brief moment.

The only worrying thing is that the learning curve of React has been a bit steep. It’s becoming increasingly difficult for developers to figure out and remember how their hooks work. To make matters worse, they are not only adding more exceptions to their rules but also changing the syntax depending on the environment execution. It becomes really challenging to understand React code.

The surprising thing is that all this is hidden behind this simplified interface where we have the illusion that we are creating pure functions.

The puzzle only works because we’re following some rules that don’t make sense. We have to be aware of the implementation details rather than consuming the declarative API. Our brain has to do a lot of gymnastics while reading React code.

So what is the solution? A react compiler seems to be the only solution to the problem as it has just started. For example if we use their performance-based optimization API like useCallback , useMemo It’s still uncertain where we want to use it vs when they will add some overhead.

Do you imagine React without the Memo API? React Forget was created to take the developer away from the memorization API. This was done in preview React Conf 2021, It’s a compiler to take away all that burden from the developer. It still has a long way to go before its release. But in my opinion it is a step in the right direction. We have reached a point where we want to consume interfaces that are then processed and optimized.

Hopefully, the React team ships and starts expanding the compiler idea. it looks like client async/await Convenience would be possible through a compiler. must collect their thoughts async/await In runtime like a generator for components. It will solve the problem by keeping some good developer ergonomics.

Meanwhile, it becomes hard to keep accumulating the logic and nuance of the ecosystem. It fails their motto and turns what was once a simple great UI library into a headache.

The original RFC proposal can be found Here

Leave a Comment