React 18 - What's New, What Changed?

React 18 - What's New, What Changed?

A new version of ReactJS is here!

React version v18.0 was released on March 29 2022. Since it is a new major release of React, it does as you would expect some new exciting features. Thankfully though it does not break your existing code. What you learned about React still applies, and the code you write still is the same.

How to update?

$ npm install react@18 react-dom@18

Before adding react 18:

import { App } from './App';
import { render } from 'react-dom';

render(<App />, document.getElementById('app')));

After adding react 18:

import { App } from './App';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('app'));
root.render(<App />);

That's the only change required. No other code changes are required. These are the tiny changes we need to make to get react 18 to work.

New features

Let's dive into the new features introduced by react 18

đź’ˇ
Concurrency
Concurrency is all about processing multiple simultaneous state updates. The below three APIs were newly introduced starting react v18.

1. useTransition() // used in functional components
2. useDeferredValue() // used in functional components
3. startTransition() // used in class components

A new concept & set of features (and APIs) that help with state update prioritisation.
Urgent state updates can be prioritised over less urgent, long-taking(blocking) updates.

Without concurrency (i.e. react before v18 )
All updates were processed in-order in which they were triggered.

How do we tell react about a state update has a lower-priority by using one of the new APIs introduced:

const [isPending, startTransition] = useTransition()

...

/**
 When you wrap a state update ex: setLoading with a startTransition hook, you are basically telling react to treat setLoading as a lower priority state update and continue and shouldn't wait for this process to finish before processing other state updates.
**/

startTransition(() => setLoading(true));

...
/**
 It says react that a older value should be used until a updated value is available instead of waiting for the UI update until the new value is available.
**/

const deferredValue = useDeferredValue(value)
However, long term, we expect the main way you’ll add concurrency to your app is by using a concurrent-enabled library or framework. In most cases, you won’t interact with concurrent APIs directly. For example, instead of developers calling startTransition whenever they navigate to a new screen, router libraries will automatically wrap navigations in startTransition.

Please note: You shouldn't go ahead and update your code to use these new functions and hooks instead we have to wait and see what new patterns and best practices emerge out of these new APIs.

New hooks for library authors.

useSyncExternalStore, useInsertionEffect & useId

These hooks solve specific problems that library authors face (ex: css-in-jss library) and they allow the authors of these libraries to utilize the work-around the problems they face and utilize the new concurrency features.

đź’ˇ
As an application developer you most likely will not use these hooks 

React 18, also introduces various automatic, "behind-the-scenes" improvements

Ex: State batching, Improved suspense API

State batching exists from react v17. Let's understand the difference from the example below:

// Before: only React events were batched.
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will render twice, once for each state update (no batching)
}, 1000);

// After: updates inside of timeouts, promises,
// native event handlers or any other event are batched.
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
}, 1000);
For more information on this you can visit this github discussion.

The suspense API upgrade,  suspense lets you declaratively specify the loading state for a part of the component tree if it’s not yet ready to be displayed:

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

This allows your to load <Comments /> component in a lazy manner, implement code splitting to only load code for a certain component when it is needed. This helps a-lot in performance.

đź’ˇ
In future releases, we can see the Suspense component work with data fetching as well giving us insights if the data is currently fetching/fetched/errored 

For more information you can visit the post by the react team.


If you liked this article please consider having a look at Tooljet, those who are not familiar with ToolJet, it is an open-source low-code application builder.
If you have any questions feel free to join the Tooljet Slack community or send us an email at hello@tooljet.com