React 18 - useTransition hook
Published: 2024-02-07 19:50:38

In March 2022 React 18 was launched with some new interesting features, in this article we'll talk about the one of the 5 new hooks on React, the useTransition hook.

## useTransition

This hook is used to control the timing of state changes that could occur based on user interactions. Simply put, useTransition hook enables state updates to occur in a way that is less blocking for user inputs resulting in a smoother user experience.

## Benefits of useTransition

**Improved User Experience:** By using useTransition, developers can ensure that the application remains responsive and interactive during resource-intensive tasks, leading to a better overall user experience.
Smooth Animations: With useTransition, you can create smooth animations that run seamlessly, without blocking the user interface.

**Better Performance:** By deferring rendering and updates, the useTransition hook helps prevent unnecessary re-renders and optimizes performance.
Enhanced Code Maintainability: useTransition simplifies the management of complex animations and transitions, making the codebase cleaner and more maintainable.

## Example: Infinite Scrolling with useTransition

Imagine you have a web application that displays a list of items retrieved from an API. As the user scrolls down, the application fetches additional data to load more items dynamically.

To provide a smooth user experience, you want to use the useTransition hook to handle the loading state and animations during data fetching.

**Step 1:** Create the app

Open [VS Code](https://code.visualstudio.com/download) and run the command bellow:

```
npx create-react-app infinity-scroll-app
```

**Step 2:** Write the following code on App.js

```
import React, { useState, useEffect, useTransition, useCallback } from "react";
import "./App.css";
import dummyData from "./dummyData"; // Assuming the file path is correct

function App() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [isFetching, setIsFetching] = useState(false);
  const [isPending, startTransition] = useTransition();

  const getItemsFromPage = (currentPage) => {
    const itemsPerPage = 10; // You can adjust the number of items per page
    const startIndex = (currentPage - 1) * itemsPerPage;
    const endIndex = startIndex + itemsPerPage;
    return dummyData.slice(startIndex, endIndex);
  };

  const fetchMoreData = useCallback(() => {
    startTransition(() => {
      setItems((prevItems) => [...prevItems, ...getItemsFromPage(page)]);
      setIsFetching(false);
    });
  }, [page, startTransition]);

  useEffect(() => {
    if (isFetching) {
      fetchMoreData();
    }
  }, [fetchMoreData, isFetching]);

  const handleScroll = () => {
    const windowHeight = window.innerHeight;
    const documentHeight = document.documentElement.scrollHeight;
    const scrollTop = window.scrollY;

    if (scrollTop + windowHeight >= documentHeight) {
      setIsFetching(true);
      setPage((prevPage) => prevPage + 1);
    }
  };

  useEffect(() => {
    fetchMoreData();
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, [fetchMoreData]);

  return (
    <div>
      <table className="table">
        <thead>
          <tr>
            <th>Name</th>
            <th>Age</th>
            <th>Country</th>
          </tr>
        </thead>
        <tbody>
          {items.map((item, index) => (
            <tr key={item._id}>
              <td>{item.name}</td>
              <td>{item.age}</td>
              <td>{item.country}</td>
            </tr>
          ))}
        </tbody>
      </table>
      {isPending && <div>Loading more items...</div>}
    </div>
  );
}

export default App;

```