A React 18 Tale
Introduction
Today we have React 18 (specifically 18.2.0), another release from React Team, but is this one more? Well… yes and no.
If you are a rookie, let me simplify the React’s main concept/goal : Take the STATE and transform to HTML and that’s all it does. I will say it: State To HTML (aka S2H… I’m joking)
And when you change that State, React handle the entire render cycle and shows you a new HTML. Simple, right?
React 18.x.x has big changes about how React works. We’ll talk about these changes and some tips on migration according to our experience . Yes, nowadays we are working on a project that used React 16.14.0 but we upgraded to React 18.2.0.
As we know React is a great solution for Frontend, but in our case, we are also working with Ionic, Capacitor, Typescript, etc. We are building Mobile App, for Android and IOS, and that is our goal. Let me say something before start, we have a new doc: https://beta.reactjs.org/
Tip: Strict Mode. Is probable, you read something about React 18, and some controversy about useEffect. That is because: with Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode. So, in production mode, you don’t have this behavior.
What else?
Technologies like NextJs and React Native, SWR, require React 18.
Particularly, they need React 18.2.0 (NextJS 13, React Native 0.70.4, SWR 2.0), but there are more, like “ultra”, “react-awesome-multiselect”, “react-waterfall-render” and “react-instantsearch” just mentioned something:
Let’s start quickly and then we’ll recap.
Automatic Batching
I want to start with this feature because it’s something that you can see with your own eyes. And it’s come out-of-the-box. React 18 handles the setStates (changes over state variable) differently. As we know the components need to “remember” things. As we read on React Docs: The set function returned by useState lets you update the state to a different value and trigger a re-render.
Don’t worry, it does what you expect, but we know every change over our State triggers an update over UI. React will store the new state, render your component again with the new values, and update the UI. Also, we know that our Components have more than one state variable. Applications have many components, so it’s very important to keep low how many times we re-render our UI.
A well-known situation is when React makes 5 or 6 setState methods inside Then block or setTimeout. When you have an asynchronous method and its result data help you to make these setStates, thanks to React 18 there is one UI update… yes, one only. Another way to see, is put some console.log in the first line in our Component, and look up how many times our console prints some message.
Here are some projects to test this feature. We recommend you open the Console, and see how many times do you run the sentences console.log(“RENDERS”);
React 18.2.0
React 16.14.0
Both projects have the same code, of course, but let me show the function of button:
const myChanges = (newState: string) => { setTimeout(() => {
setA(newState);
setB(newState);
setC(newState);
setD(newState);
setE(newState);
setF(newState);
setG(newState);
setH(newState);
setI(newState);
setJ(newState);
}, 1000)}
By clicking on the button, you can see how many times the console log is printed: 2 against 10.
React 18.2.0
React 16.14.0
However, this is a case, only built for test purposes. In these examples, of course, we see a big difference. I know it is weird, but remember it is just an example.
You can see the same behavior if we use a fetch (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
A setState Mechanism
One Word about setState:
What do we know about the setState method? Well, I must be honest, every day I discover something about it. I invite you to see these samples:
React and setState
React State and Data types of ECMAScript
I have some questions for you:
- Which is the best way to save a value that depends on old value?
- Is there a difference between saving a String and an Object ?
- Is the setState method a synchronized method?
- Are you responding to these questions thinking about the UI?
Simple Double setState vs State with Double Arrow setState
State with Primitives vs State with No Primitives
Tip: Closures
(from https://developer.mozilla.org )
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
When you see a function inside another function pay close attention to the variables, i.e., which type of value you deal it (and of course, its scope).
Concurrent Mechanism
Some people think that this is the most important feature, Concurrent Rendering. If you think about it, it is not a feature but a new mechanism (behind-the-scenes), and as you expect you will note a better performance here too. Today, you can’t interrupt once an update starts rendering. However, with React 18 you can! And of course, as React Docs say: React guarantees that the UI will appear consistent even if a render is interrupted. This means the UI can respond immediately to user input even if it’s in the middle of a large rendering task, creating a fluid user experience.
At this moment you think how can we stop or more importantly why? Maybe you don’t need to stop at anything, but maybe you need to say to React what order or sentence is more important than others. I mean, you can indicate for example, which update state is more important, or is better say you can indicate which sentences are not important, therefore you are saying which sentences can stop and rerun after other parts of your app were updated for example. Here the important thing is the user will notice nothing, even more the experience will be better. And now we can talk about a new hook. useTransition, and how it can help us to avoid some UI freeze and therefore get a better user experience.
Tip: Fast
If you think about it, it is a big change. You change from sequential mode to parallel mode, if we can say that. So my recommendation is, search your code, verify consistency about the moment that you want to use a state variable, and check if this state variable is fine. Why? The main reason is, until now, your code works because the sequential steps let you use some state variable, but now, it’s faster and parallel, and maybe you find some surprises.
Transitions
When we talk about a new hook, useTransition, our intention is to talk about startTransition. Transition you say? Yes I know, it is a particular name.
const [isPending, startTransition] = useTransition();
const [urgentCount, setUrgentCount] = useState(0);
const [count, setCount] = useState(0);
const handleClick = () => {
setUrgentCount(c => c + 1);
startTransition(() => {
setCount(c => c + 1);
})}
I just say, startTransition helps you indicate which sentences are secondary sentences, are not urgent, and finally, we can say something that can be interrupted. For what? For one purpose, to let more important sentences be the star of this movie.
Now, we can separate in two dimensions, for example, which setStates are more urgent/important for our App/Users
New Hooks
Some news about Hooks
Hook useId
The useId hook has one recommendation: Do not call useId to generate keys in a list. It is special for cases where you are working with server rendering.
Hook useSyncExternalStore
The useSyncExternalStore hook, lets you subscribe to an external store, is intended for Third-party state management libraries development. The official docs say: The useExternalSyncStore API is mostly useful if you need to integrate with existing non-React code. But I think it is a tool that deserves to be analyzed and used by every developer. In such a case, it needs to be reviewed in another publication focused on it. There are awesome videos about it on youtube.
Hook useDeferredValue
The useDeferredValue hook, helps us with the large and heavy components that can block our experience and get a feeling of slowing down. You can use it when useMemo has a dependency that has changed a lot.
Hook useInsertionEffect
The useInsertionEffect hook is executed before useLayoutEffect, and useLayoutEffect is called before useEffect. React Docs say us that you will need it in cases such as css-in-js library development
Hook useTransition
The useTransition hook, just as we have talked about it above.
More Stuffs
Take a look around to new React.FC, it changes respect to children prop. You probably get a new message:
Property ‘children’ does not exist on type
Now you need to be more expressive.
import * as React from 'react';
type Props = { children?: React.ReactNode };
const MyComponent: React.FC<Props> = ({children}) => {
//your code
}
And finally, say Goodbye to IE11, so if you need to be compatible with it, keep on React 17.
Conclusion
We talk about a lot of things, but if we can resume all of it today we will have more performance thanks to React 18. Our APP is 40% or 50% faster. Of course, it depends on each project. You know that sometimes all of these updates are not always a change for the final user. But in this case, I can say it is different, the final user can notice slightly better performance.