React 18.0 (alpha)

Thursday, August 26, 2021 min read

The most awaited version of React 18 is coming soon. 🥳

React team has finally revealed the alpha version of React 18 and its plan, though the official launch is still pending, they also created a Working Group to prepare the community for gradual adoption of new features in React 18.

What’s coming in React 18

React 18 will include out-of-the-box improvements (like automatic batching), new APIs (like startTransition), and a new streaming server renderer with built-in support for React.lazy.

Since concurrency in React 18 is opt-in, there are no significant out-of-the-box breaking changes to component behavior.

You can upgrade to React 18 with minimal or no changes to your application code, with a level of effort comparable to a typical major React release.

You can try all of the next features that I will present installing the React 18 alpha.

npm install react@alpha react-dom@alpha

Working with the community

The goal of the React 18 Working Group is to prepare the ecosystem for a smooth, gradual adoption of React 18 by existing applications and libraries. The Working Group is hosted on GitHub Discussions and is available for the public to read.

Root API

React 18 ships two root APIs, which we call the Legacy Root API and the New Root API.

This is the existing API called with ReactDOM.render. This creates a root running in "legacy" mode, which works exactly the same as React 17.

Before release, we will add a warning to this API indicating that it’s deprecated and to switch to the New Root API.
import ReactDOM from 'react-dom';

import App from 'App';

const container = document.getElementById('#root');

ReactDOM.render(<App />, container);

The new Root API is called with ReactDOM.createRoot. This creates a root running in React 18, which adds all of the improvements of React 18 and allows you to use concurrent features. This will be the root API moving forward.

import ReactDOM from 'react-dom';
import App from 'App';

const container = document.getElementById('#root');

const root = ReactDOM.creatRoot(container);
root.render(<App />);

Automatic Batching

When groups of React multiple state updates into single render for improved performance is called batching.

For instance, React has always batched these into one re-render if you have two state updates inside the same click event.

Though, React wasn't constant about when it batches updates. It is because React used to only batch updates during a browser event (like click), but here we're updating the state after the event has already been handled (in fetch callback):

function App() {
    const [count, setCount] = useState(0);
    const [flag, setFlag] = useState(false);

    function handleClick() {
        setCount((c) => c + 1); // Does not re-render yet
        setFlag((f) => !f); // Does not re-render yet
        // React will only re-render once at the end (that's batching!)

        fetchSomething().then(() => {
            setCount((c) => c + 1); // Causes a re-render
            setFlag((f) => !f); // Causes a re-render
        });
    }
}

In the automatic batching (after updating your system to React 18), no matter where the states originate, it will always be re-rendered once.

function App() {
    const [count, setCount] = useState(0);
    const [flag, setFlag] = useState(false);

    function handleClick() {
        setCount((c) => c + 1); // Does not re-render yet
        setFlag((f) => !f); // Does not re-render yet
        // React will only re-render once at the end (that's batching!)

        fetchSomething().then(() => {
            // React 18 and later DOES batch these:
            setCount((c) => c + 1);
            setFlag((f) => !f);
            // React will only re-render once at the end (that's batching!)
        });
    }
}

It is best for performance because it avoids unimportant re-renders. It also prevents the component from rendering half-finished states where a single state variable was updated only.

In Case if You Don’t Want to Batch you have to use flash sync to re-render the component.

import { flushSync } from 'react-dom';

function handleClick() {
    flushSync(() => {
        setCounter((c) => c + 1);
    });
    // React has updated the DOM
    flushSync(() => {
        setFlag((f) => !f);
    });
    // React has updated the DOM
}

Transition

Transition API is an incredible feature that's coming with React 18. It allows the users to solve the frequent update's issues on the large screens.

Look to that example...

Type in the input field that filters the data list.

You have to solve the area's value in the state to separate the data and control the input field value.

Now, with the new startTransition API has solved that issue by allowing to mark updates as transitions.

import { startTransition } from 'react';

// Urgent: Show what was typed
setInputValue(input);

// Mark any state updates inside as transitions
startTransition(() => {
    // Transition: Show the results
    setFilterValue(input);
});

Suspense and SSR Support

To be aware SSR lets you generate HTML from React components on the server and that HTML to the users.

In a typical React application, the SSR occur in several steps.

  1. On the server, fetch date for the entire application.
  2. Then, on the server, render the entire application to HTML and send it in the response.
  3. On the client, load the JavaScript code for the entire App.
  4. Finally, on the client, connect the JavaSript logic to the server-generated HTML for the entire application which is called hydration.

The problem is that each step had to finish for to entire application at once, before the next step start, is not efficient if some part of your application is slower than the others. We have to render all HTML at once, and all JavaScript at once, and everything is then hydrated and is interactive.

But now, React 18 has tried to solve this issue.

<Suspense> component is revolutionized to break down the application into smaller independent units that pass through the steps mentioned above. As a result, users will see the application content quickly and start interacting much faster with it.

Concurrency

React 18's central theme is concurrency that is the ability to perform multiple tasks simultaneously.

When a user is typing and clicking on buttons, animation also renders there within the React's context and React has to handle all the function calls, hook calls, and event callbacks, some of which can even co-occur.

So, if React gives all its time rendering animation frames, users will think that the application is stuck since it won't be reacting to their inputs.

Now React, working on a single-threaded process, has to merge, reorder and prioritize these events and functions to provide users an optimum and quality experience.

For this, React uses a dispatcher internally, responsible for prioritizing and requesting these callbacks.

Before React 18, users had no way to control the invocation order of these functions.

But now, it's providing some control of this event loop to the user with the Transition API.

How to upgrade

Upgrading to React 18 will be the same as any React major such as 16 and 17 (by updating to the latest React 18 release with the additional step of switching from ReactDOM.render to ReactDOM.createRoot.

After upgrading, we recommend testing your application as you normally would with an upgrade to flush out any of the issues surfaced by the out-of-the-box improvements.

Summing Up!

React 18 release includes out-of-the-box improvements to existing features that will help the developers in improving the application speed and efficiency.

It is also the first React release to add support for Concurrent Features, which let you improve the user experience in ways that React didn't allow before.