Understanding Transition in React 18 Using useTransition Hook

React 18 introduces ‘Transition’ to improve the application responsiveness by optimizing the performance.

Nivetha Krishnan
Bits and Pieces

--

What is Transition?

React 18 has introduced a new concept called transition to help distinguish between urgent and non-urgent updates.

Urgent updates like typing, clicking, or pressing are expected to respond immediately on the screen.

Non—urgent updates are displaying the list of users, where a slight delay can be acceptable, and doesn’t expect to see every intermediate value on screen.

useTransition Hook:

React 18's new hook useTransition lets you update the state without blocking the UI.

Before deep diving into how to use the latest hook, let’s have a deeper understanding of the problem its solves.

Let’s Talk About the Problem

Consider an application where you render a huge list of users and a textbox to search any of the users by their names. Let’s imagine the following code component.

import React, { useState, useTransition } from 'react';

export default function App({ users }) {
const [searchUsers, setSearchUsers] = useState('');
const [filterUsers, setFilterUsers] = useState(users);

const handleChange = ({ target: { value } }) => {
setSearchUsers(value);
setFilterUsers(users.filter((item) => item.name.includes(value)));
};

return (
<div className="container">

<div>
{
users.length !== filterUsers.length
? `${filterUsers.length} matches`
: null
}
</div>

<input
onChange={handleChange}
value={searchUsers}
type="text"
placeholder="Type a name"/>

<div className="cards">
{filterUsers.map((user) => (
<div class="card">
<div className="profile">
<img
src={user.avatar}
alt="avatar" />
</div>
<div className="body">
<strong>{user.name}</strong>
</div>
</div>
))}
</div>

</div>
);
}

We have two states searchUsers and filterUsers. The searchUsers state will help manage the text, entered in the textbox to filter the user list. The filterUsers is the state variable for managing the filtered user list.

The handleChange is an event handler function for the search box so that we can handle the event every time someone types in it. Both the states are updated during this event handler. The first state update will update the search term value on the search textbox. The second state update will update the user list.

By default, all the state updates are urgent in React and both the state change occur almost simultaneously. React will batch the change and update them together which is the best feature of React. But what if we are dealing with a huge list of users? Here comes the problem

The first state change (searchUsers ) will take a short time to update the value on the search textbox, but the second state (filterUsers ) may take a long time to filter out the huge list of users. Since React batch and updates the changes together, the quicker state update will wait for the longer state to complete. This may lead to a situation where the typing in the search textbox will lag which is not a good user experience. The useTransitionis here to rescue.

Solution: The useTransition Hook

With this hook, we can specify any state update as non-urgent. These non-uregentstate updates will occur simultaneously with other urgent state updates, but the component rendering will not wait for the non-urgent state updates.

The useTransition hook Structure:

const [isPending, startTransition] = useTransition();

The useTransition hook returns an array with two elements:

isPending: It is a boolean-type variable that returns true when the second element startTransition executes and false when the execution is completed.

startTransition: A plain JavaScript function that takes a callback function as an argument that contains code related to non-urgent state updates.

Let’s now modify our code using the useTransition hook.

The first thing is to import the hook to use it.

import React, { useState, useTransition } from 'react';


const [isPending, startTransition] = useTransition();

Next, we have to modify the handleChange method where the state updation happens. Now we are marking the state change of the user-filtered list as non-urgent by placing it inside the callback function of startTransition.

const handleChange = ({ target: { value } }) => {
setSearchUsers(value);
startTransition(() => {
setFilterUsers(users.filter((item) => item.name.includes(value)));
});
};

The last thing is to update the isPending variable to ensure we show a loader when the startTransition function executes.

{isPending ? (
<div>Loading...</div>
) : (
<div className="cards">
{filterUsers.map((user) => (
<div class="card">
<div className="profile">
<img
src={user.avatar}
alt="avatar" />
</div>
<div className="body">
<strong>{user.name}</strong>
</div>
</div>
))}
</div>
)}

💡 Note: If you wanted to create custom hooks using useTransition and other react hooks, you can reuse them using Bit. Bit lets you publish, version, and reuse custom hooks across multiple projects with a simple bit import your.username/yourCustomHook command.

Learn more here:

The Complete Code:

import React, { useState, useTransition } from 'react';

export default function App({ users }) {
const [searchUsers, setSearchUsers] = useState('');
const [filterUsers, setFilterUsers] = useState(users);
const [isPending, startTransition] = useTransition();

const handleChange = ({ target: { value } }) => {
setSearchUsers(value);
startTransition(() => {
setFilterUsers(users.filter((item) => item.name.includes(value)));
});
};

return (
<div className="container">

<div>
{
isPending ? (
<div>Loading...</div>
) : (
<p>
{
users.length !== filterUsers.length
? `${filterUsers.length} matches`
: null
}
</p>
)
}
</div>

<input
onChange={handleChange}
value={searchUsers}
type="text"
placeholder="Type a name" />

{
isPending ? (
<div>Loading...</div>
) : (
<div className="cards">
{filterUsers.map((user) => (
<div class="card">
<div className="profile">
<img
src={user.avatar}
alt="Avatar" />
</div>
<div className="body">
<strong>{user.name}</strong>
</div>
</div>
))}
</div>
)}
</div>
);
}

Conclusion:

With the help of this hook, the component is improved to handle the non-urgent state updates and rendering much more effectively.

Build React Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

--

--