Let’s start with the last question…
Upgrading to React 18 in practice
Let’s start by getting our hands dirty. I have an old repository that I set up when I first did the React tutorial. The repository dates back to a pre-Hooks version of React 16. It feels like a kind of worst-case scenario. This will allow us to see what happens when we update React from version 16 to 18.
Spoiler alert: It’s fast and it’s painless. And that was exactly the whole point of the "featureless update” that was React 17.
Let’s see what happens in practice. To start out, if we’re under NPM, we’ll run :
npm install email@example.com firstname.lastname@example.org
And if we are using yarn we’ll run:
yarn add email@example.com firstname.lastname@example.org
And when I launch the project everything seems to be working just fine. But if we open the console, we see the following message:
"ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it’s running React 17”
What does this mean? It means two things.
The first thing is that the React team has deprecated the code that initializes React. That code is no longer the right way to do things if you want to take advantage of React’s new features.
The second thing is that React handles deprecation very nicely.
Sure, my initialization is not ideal. But React isn’t going to cause me any grief. My code still works without any problems, as you can see. So, how do I fix this issue? If I look at my code, the import and initialisation section reads:
import ReactDOM from "react-dom”; //…. and then const root = document.getElementById("root”); ReactDOM.render(<App />, root);
The initialization method has changed, as has the import. Now, in React 18, we need to import the client-side version of React-dom, by doing:
import ReactDOM from "react-dom/client”;
This is due to the existence of a server-side version of the code. And the initialization is now done in two steps:
// first const root = ReactDOM.createRoot(rootElement); root.render(<App/>);
The setup is now split into two steps, to differentiate client-side and server-side operations or to allow us to do a hydrate on the root instead of render. Now, if I just do these two changes, my editor spits out a Typescript error. If you’re using typescript, you also need to update the types by doing:
yarn add @email@example.com @firstname.lastname@example.org
Once these small fixes are done, everything runs …. without any problems. So you can update React to version 18 without any particular worries. But is there any point in doing so? Let’s talk about… what React 18 brings to the table.
What’s new in React 18?
And the reality is that there are few visible changes. More so than in the "Featureless Update” that was React 17. But still. Here, in Version 18, we see that the React team is working hand in hand with the Next JS teams. And, to a lesser extent with Remix.
Because truth be said, you and I won’t be using most of the new features in our code. Instead, we will be using third-party libraries or frameworks that call on them. But let’s talk about the improvements that could have a significant effect. First of all, there is the notion of…
Automatic render batching in React 18
Now, Let’s imagine we are managing two states. One, for example, represents a score, and the second represents available actions. The user taking an action will increase the score and reduce available actions. So in our code, we’d write something like:
setScore(score +1); setActions(actions — 1);
In other words, we are updating the state twice. And up until now, React would detect two separate state updates and trigger two renders. But React 18 introduces the notion of automatic "batching”, i.e. grouping of render tasks. Despite the two separate state updates, the app will re-render once.
It’s worth making the most of. Although I don’t know how many of you have experienced performance problems. And in theory, this… lack of usefulness will be even more the case for the other new features.
Let’s take a quick look at them. First of all, we have…
To frame things in simple terms, it’s about breaking rendering monolith.
It’s a case of saying: "Look at this calculation, for that part of the UI. We can defer that to later when we have available computing power. That piece of UI can use an old value while waiting.” For that, there is the notion of "transition” which embodies a non-urgent change.
Let’s take the case of a filtered list. The user starts typing text into the filter. They expect the interface to immediately display the typed text. And the list to show that the filtering is taking place.
But the user understands if it takes a few seconds to display the filtered result. So for the same action, we have two distinct reactions. One urgent reaction and a non-urgent reaction. This non-urgent reaction is what the React team calls a "transition”.
So we now have access to a Hook (called
useTransition) and function (called
startTransition*) that embody this.
But as the blog post announcing the release of React says:
" 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..”
This is even more true for our next topic…
Server-side rendering and streaming (which are still a work in progress) are intended for frameworks like NextJS or Remix. You likely won’t have any direct use for this feature. Unless you’re building a server-side framework of your own, of course.
But this feature promises great things for NextJS or Remix.
This brings me to my next point
Should you upgrade to React 18?
Should we upgrade to React 18? The answer is yes. Not because it will revolutionise anything in our code. But because there is no good reason not to, since it is so painless.
If your applications are computationally intensive, the new features will come in useful. Even simple applications can profit from the optimisations.
The real benefit will be via libraries like React Router and frameworks like NextJS or Remix. They’ll make use of React 18’s new features. And in doing so, they’ll offer improvements in user and developer experience that would be a shame to miss!
So to take advantage of React 18, I recommend you use the NextJS or Remix frameworks.