Trying Out React 18 Alpha Release

Nathan Sebhastian
Bits and Pieces
Published in
7 min readJun 11, 2021

--

The React Team has recently released a new alpha version of React 18. The main addition to this new version is concurrent features that improve the performance of your React application.

The notable features you can already try in React 18 are as follows:

  • The new ReactDOM.createRoot() API replacing ReactDOM.render()
  • Batching improvements for fewer renders
  • SSR support for <Suspense> component
  • startTransition API for non-urgent state updates

To try out React 18 Alpha in your project, you can install the @alpha version from NPM or Yarn:

npm install react@alpha react-dom@alpha
# or
yarn add react@alpha react-dom@alpha

If you bootstrap your React app using a Create React App, you may encounter an error could not resolve dependency caused by react-scripts as follows:

Could not resolve dependency:
peer react@">= 16" from react-scripts@4.0.3

You can use the --force flag so that you can update both react and react-dom libraries:

npm install react@alpha react-dom@alpha --force

This tutorial will help you learn the new features already present in React 18 Alpha that I’ve been able to try myself.

ReactDOM.createRoot() API explained

The ReactDOM.createRoot() method replaces the ReactDOM.render() method that you normally use as the entry point of your React application.

The method is created to prevent React 18 update from crashing your application. The new method also allows you create a production build using React 18 Alpha and compare the performance with React 17.

Here’s an example of how to use the createRoot() method:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const container = document.getElementById('root');// Create a root.
const root = ReactDOM.createRoot(container);
// Render the top component to the root.
root.render(<App />);

When you update to React 18, an error log will appear in the console when you use .render() as your entry point, telling you to switch to the new Root API instead.

You can try React 18 features only after you use the createRoot() method.

For more information, you can visit createRoot discussion page here.

Automatic batching improvements for rendering

As you may already know, React is a library that re-renders the UI as a result of state changes.

For example, when you change the value of an arbitrary state from true to false , React should “react” by re-rendering the UI, adjusting what you see on the screen according to the code you’ve written.

The following <App> component will render a black or red colored header according to the color state value:

function App() {
const [color, setColor] = useState(false);
function handleClick() {
setColor((color) => !color); // react re-renders
}
return (
<div>
<button onClick={handleClick}>Change color</button>
<h1 style={{ color: color ? "red" : "black" }}>Hello</h1>
</div>
);
}

Each time the setColor() method is executed, React will immediately re-render the UI.

Batching is mechanism used by React to group multiple state updates into one re-render. With batching, you will avoid unnecessary renders and optimize the rendering process

Getting back to the <App> component example, let’s add another state that gets updated when the handleClick() method is called:

function App() {
const [color, setColor] = useState(false);
const [clickCount, setClickCount] = useState(0);

function handleClick() {
setColor((color) => !color);
setClickCount((click) => click + 1);
}
return (
<div>
<button onClick={handleClick}>Change color</button>
<h1 style={{ color: color ? "red" : "black" }}>
Hello, your click count is {clickCount}
</h1>

</div>
);
}

Without the batching mechanism, the code above will re-render the UI twice each time the handleClick() method is called. First to update the color, then one more time to update the click count.

However, the batching mechanism implemented in React 17 was not consistent. The batching won’t happen when you call the state update methods from a callback.

For example, suppose you fetch data from an API first before calling the setColor() and setClickCount(). The batching mechanism won’t kick in and React will re-render the UI twice:

function handleClick() {
fetchUserData().then(() => {
setCount(c => c + 1); // Causes a re-render!
setFlag(f => !f); // Causes a re-render!
});
}
function fetchUserDate(){
// code omitted for brevity...
}

The same thing happens when you put the state update methods inside a setTimeout() callback:

function handleClick() {
setTimeout(() => {
setColor((color) => !color);
setClickCount((click) => click + 1);
}, 1000);

}

React 18 solved the problem above by improving the batching mechanism.

Now the batching gets triggered when more than one state update methods are called from inside promises, setTimeout, native event handlers, or any other event that were not batched by React previously.

The discussion page for this feature can be found here.

SSR support for <Suspense>

The <Suspense> component is a feature of React library that allows you to wait for some code to load by adding a fallback component to render before the code is loaded.

Here’s an example of <Suspense> in action:

<Suspense fallback={<LoadingSpinner />}>
<UserProfile />
<Suspense />

You can read about <Suspense> in depth from React documentation.

In React 18, the <Suspense> feature is supported even when you render your components on the server using SSR. This update allows you to wrap server rendered components inside a <Suspense> component.

Any server side components wrapped inside <Suspense> will be streamed as HTML using the fallback component first, and once the component is ready, React will send new bits of HTML to replace the fallback component.

For example, suppose you have an <Article> and a <Comments> components as follows:

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

When rendered from the server, the <Article> component will be prioritized and the <Comments> component will be replaced by the fallback component <Spinner> . Once the <Comments> component is rendered on the server, React will send it to the browser, replacing the <Spinner> component.

For a deeper explanation on SSR and Suspense, you can visit the following GitHub discussion page.

The startTransition API for non-urgent state updates

The startTransition API is a new feature in React 18 designed to help your application stay responsive during state update that requires heavy computation power to render the UI.

One example of such update is when you create an input box that filters a list of data. The state update requires your React app to compute and display only data that matches the filter.

You may have two state update methods: one to handle the input value change and the other to handle the filter query.

// Set the input value state
setInputValue(input);

// Set the search query input. Reflected later on the UI
setSearchQuery(input);

When the amount of elements to filter increases, the computation to filter the elements are also increased. This can cause your app to be sluggish or even freeze while the computation is being done.

To mitigate this issue, React allows you to mark certain updates as transitions.

Transition updates are handled as non-urgent updates, allowing React to prioritize on urgent updates first.

Back to the example, the update to the search query may be delayed by wrapping it inside the startTransition API as follows:

import { startTransition } from 'react';// Urgent: Show what was typed
setInputValue(input);
// Mark any state updates inside as transitions
startTransition(() => {
setSearchQuery(input);
});

The updates wrapped in startTransition will be interrupted when more important updates are triggered.

In the above example, the transition update for the search query will be stopped when the user types multiple multiple characters in a row. This optimizes React re-render performance and removes unnecessary computation for stale updates.

You can find more information on startTransition API here.

Conclusion

While several features mentioned in React 18 introduction page hasn’t even been released yet (useDeferredValue and <SuspenseList> for example)

React 18 Alpha already brings several interesting features to React that improves the concurrency power of the library. You can try it by installing the @alpha build of react and react-dom library.

There’s also no update on React Server Components yet, but the new concurrent features like SSR support for Suspense and streaming HTML from the server may help in implementing Server Components later.

The release timeline for React 18 is as follows:

  • Alpha version to gather feedback and offer support for React Working group (Available today)
  • Public Beta for everyone else to try (at least several months after Alpha version release)
  • Release Candidate (RC) build several weeks after the public beta version
  • The stable release will be several weeks after RC

Thank you for reading this article. Feel free to try out React 18 yourself, but be warned that there may be breaking changes depending on how complex your React application is.

Build Great Design Systems and Micro Frontends

Take frontend development to the next level with independent components. Build and collaborate on component-driven apps to easily unlocks Micro Frontends, and to share components.

OSS Tools like Bit offer a great dev experience for building and composing independent components, and build solo or together with your team.

Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

--

--

Web Developer and Writer. Sharing what I learn on productivity and success.