5 minutes to figure out useTransition and useDeferredValue in React 18
gankai
Posted on July 25, 2024
React 18 introduces a crucial new concept called "Concurrent". Concurrent involves performing multiple state updates simultaneously, which is arguably the most important feature in React 18. Alongside concurrency, React 18 also introduces two new hooks called useTransition() and useDeferredValue(). They both help to lower the priority of state updates, but the question is, when should they be used?
What is Concurrent React?
According to the official React18 Docs, Concurrent React is a new behind-the-scenes mechanism that allows React to prepare multiple versions of the UI simultaneously. You can think of concurrency as an implementation detail—its value lies in its features. Concurrency can be defined as the ability to perform many tasks simultaneously. It is not a feature itself but rather a behind-the-scenes capability that allows React to prepare many UI instances concurrently. React's creators believe that developers should focus on how React features will help them achieve the user experience they want to provide, while React will figure out how to deliver that experience.
Concurrent React is more than just an implementation detail. It is a significant upgrade to the core rendering architecture of the framework. Hence, understanding how it works in React 18 is crucial.
Setting Up the Project
For this article, I created a Github repository where I built a simple demo product application that allows users to view products by entering a product number. First, you must clone the Github repository containing all the project starter files. To clone the Github repository, go to your terminal and run the following command:
git clone <https://github.com/geekskai/react18-feature.git>
After completing the cloning process, go to the project folder, open it in your code editor, navigate to the editor terminal, and run npm install or yarn install to install the scripts and dependencies. Then, to start the project, run npm start.
Once the application is up and running, it should look like this:
Implementing useTransition()
When you first enter the product number to fetch the product, you will notice that it updates almost immediately, even if it is a very long list of 10,000 items to browse through.
Now, when you go to the Developer Tools section of Chrome, go to the performance tab, turn on CPU throttling, and reduce the CPU speed by 4x, the issues start to appear.
If you try entering the product number after reducing the CPU speed, you will notice that the updates become slower and noticeably laggy. Even the entire interface looks and feels sluggish, especially the input field, which now feels unresponsive as we type and delete. And this is definitely not a good user experience.
You can't see me typing, but the interface responds slowly, as shown above. Even before React18, a standard solution was not to handle 10,000 items or products (in our case) at once. You could use pagination or any other technique, or filter on the server-side instead of the client-side. These are all possible solutions you can consider when you encounter such issues.
However, if you need to perform such operations on the client-side, i.e., in your client code, then with React18, you now have some tools to provide better perceived performance to users by deferring some state update operations by telling React that some updates have higher priority than others. This is the idea behind the concurrency introduced in React 18 and the related hooks and functions.
useTransition() tells React that some state updates have a lower priority, i.e., every other state update or UI rendering trigger has a higher priority. When we call useTransition(), we get an array with two elements: an isPending boolean value that indicates whether a low-priority state update is still pending, and a startTransition() function that you can wrap around state updates to tell React that this is a low-priority update.
Let's see how to use the useTransition() hook. First, go to the App.js file and edit the code as follows:
function App() {
const [isPending, startTransition] = useTransition();
const [filterTerm, setFilterTerm] = useState('');
const filteredProducts = filterProducts(filterTerm);
function updateFilterHandler(event) {
startTransition(() => {
setFilterTerm(event.target.value);
});
}
return (
<div id="app">
<input type="text" onChange={updateFilterHandler} />
<ProductList products={filteredProducts} />
</div>
);
}
Since startTransition() wraps the setFilterTerm() state update function, React gives this state update code a lower priority. This ensures that the input field remains responsive and instantly reacts to keystrokes in the demo application. Without using useTransition(), the application might become unresponsive, especially on slower devices. When you enter the product number, you will see that the code now responds more quickly and with less delay, even with the CPU throttled by 4x. You can try this on your system and see the results.
However, you should not start using startTransition to wrap all state updates. Use it only when your user interface is slow, especially on older devices, or when you have no other solution available. This is because it incurs additional performance overhead.
What Does isPending Do?
isPending tells you whether there are some state updates that are still pending (React has not yet executed and are of lower priority). You can use isPending to update the UI to show some fallback content while waiting for major state updates to complete. You can see this in the following code in the App.ts file:
return (
<div id="app">
<input type="text" onChange={updateFilterHandler} />
{isPending && <p style={{color: 'white'}}>Updating list..</p>}
<ProductList products={filteredProducts} />
</div>
);
}
After implementing the code, you should be able to see the following while running the application:
Therefore, this is another feature you can use while working with useTransition(). Also, note how responsive it is after implementing the useTransition() feature.
❤️ After reading, do three things:
If you find this content inspiring, I would like to invite you to do me a small favor:
Like it, so more people can see this content, and it's easier for you to find it later (bookmarking without liking is just playing around -_-);
Follow us for more great articles from time to time;
Check out other articles as well;
🎉 Feel free to write your learning experiences in the comments section and discuss with me and other students. If you find it helpful, you are also welcome to share the article with your friends.
Posted on July 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.