useTransition and useDeferredValue in React 18
useTransition and useDeferredValue are two new hooks React introduced with React 18 earlier this year. These two hooks make use of React’s concurrent rendering to allow developers to provide a better user experience in their applications. In this article, we will be taking a good look at these two hooks.
Concurrency
Before React 18, rendering was synchronous. This meant that once React started rendering, nothing could stop it until it completed rendering the component. However, with concurrent rendering, React can pause the rendering and continue with it later or abort the rendering altogether. This is an important new implementation that allows us to provide a more fluid user experience to our users. Let’s see how with an example.
Let’s say we have an array containing numbers from 0 to 19,999. These numbers are shown on the UI in a list. The UI also has a textbox that allows us to filter these numbers. For example, I can filter out the numbers that start with 99 by entering the number 99 into the textbox.
Since there are 20,000 elements in the array, filtering is going to be a bit of a time-consuming process. We can observe this when we try to enter a number into the textbox. There will be a lag in the typed value appearing in the textbox because the render following every keystroke is going to take some time. Now, what if we can delay the filtering function and allow the typed text to appear in the textbox immediately? This is what concurrency in React accomplishes.
When we use concurrency, we can either pause or cancel the rendering that involves filtering the numbers, and give priority to updating the state of the textbox. Now, let’s see how we can implement this.
useTransition
We can use the useTransition hook to tell React that a certain state change will result in an expensive rendering. React will then deprioritize this state change allowing other renderings to take place faster providing a very responsive UI. Such expensive renderings are called transition updates and the ones that should take place immediately are called urgent updates. Usually, typing, clicking, and pressing are considered urgent updates as they should provide users with an immediate response to ensure a good user experience.
By default, every state change is considered an urgent update. We can specify a transition update by using the useTransition hook. When we call the useTransition hook, we get an array with two elements. The first element is a boolean value that specifies if the rendering is pending or not. The second element is the startTransition method.
This method accepts a callback function. Any state update we perform within this callback function will be considered a transition update.
To make use of this, we can have two state values. One state value can be bound to the textbox and the second state value can be used to filter the array. We can write a handler function to capture the input text on change. We can update the first state value immediately and update the second state value inside the startTransition callback function.
So, when I first enter a key, React will update the first state, perform a render and then update the second state and start the more expensive render. Now, if I enter another key, since React knows that this is a transition update, it will abort the transition rendering, perform the urgent render, and then start a new transition render. This means that the user will not experience any lag while typing into the textbox.
useDeferredValue
This hook is very similar in functionality to the useTransition hook. While we use the useTransition hook to tell React that a certain setState method will trigger a transition update, we use the useDeferredValue hook to tell React that a received prop value will trigger a transition update.
This will be useful when we have no control over the setState method. The previous example can be modified to explain this hook’s use case. Let’s assume the textbox lies in a parent component and we receive the input text as a prop. And let’s assume that we have no control over the parent component. So, we can’t use useTransition here.
Instead, we can use the useDeferredValue hook to tell React that the passed prop value will trigger a transition update. We just need to pass the prop value as an argument into the useDeferredValue hook and use the returned value in our component. React will then do the same thing it does with the useTransition hook by deferring this expensive render to allow the urgent updates to take place faster.
In conclusion, by allowing us to distinguish between urgent updates and transition updates, React 18 gives us more control over the priority of different state updates. This allows us to prioritize more urgent renders while delaying the more expensive renders, resulting in a better user experience to users.
[…] With this mental model, let’s see how we can address most of the common use cases in another article. […]