useTransition là một hook trong React, được dùng để đánh dấu một phần update là “chậm” (non-urgent), nghĩa là nó có thể hoãn lại một chút để React ưu tiên những update quan trọng hơn như phản hồi UI ngay khi người dùng gõ, bấm, di chuyển chuột.

NOTE

useTransition giúp React giữ cho UI mượt khi thực hiện các tác vụ tốn thời gian (render danh sách lớn, filter nhiều dữ liệu, chuyển trang)

Ví dụ

import { useState, useTransition } from "react";
 
function SearchList({ items }) {
  const [query, setQuery] = useState("");
  const [filtered, setFiltered] = useState(items);
  const [isPending, startTransition] = useTransition();
 
  function handleChange(e) {
    const value = e.target.value;
    setQuery(value);
    
    startTransition(() => {
      // Giả sử danh sách lớn => filter tốn thời gian
      const results = items.filter(item => item.includes(value));
      setFiltered(results);
    });
  }
 
  return (
    <div>
      <input value={query} onChange={handleChange} placeholder="Search..." />
      {isPending && <p>Loading...</p>}
      <ul>
        {filtered.map(item => <li key={item}>{item}</li>)}
      </ul>
    </div>
  );
}

Ở đây nếu không sử dụng useTransition, React phải render lại toàn bộ danh sách mỗi lần chúng ta gõ thêm 1 ký tự UI bị khựng, lag.

Với việc sử dụng useTransition, React ưu tiên cập nhập input trước, sau đó mới chạy phần filter cảm giác vẫn mượt.

Tìm hiểu về cách hoạt động

Vậy useTransition hoạt động thế nào?

Ta cùng đi từng lớp, từ mức ý tưởng, cách React quản lý priority, đến cách hook hoạt động nội bộ.

Mục tiêu của useTransition

React muốn tách ra hai loại update

  • Urgent (cấp cao) nhập input, bấm nút, di chuyển chuột, …
  • Transition (cấp thấp) render danh sách lớn, load dữ liệu, filter, navigate

Trong React Fiberscheduler cho phép gán độ ưu tiên khác nhau cho từng update, vã tạm hoãn update chậm nếu cần.

useTransition thực ra là một hook “bọc” quanh hệ thống scheduling này. Chúng ta có thể xem qua pseudo-code sau:

function useTransition() {
  // React lưu "priority" hiện tại của context
  const currentPriority = getCurrentUpdatePriority();
 
  // Khai báo state để biết đang pending không
  const [isPending, setPending] = useState(false);
 
  // Hàm startTransition
  const startTransition = (callback) => {
    // Bắt đầu transition: bật pending
    setPending(true);
 
    // Chuyển sang chế độ “transition priority”
    const prevPriority = currentPriority;
    setUpdatePriority(TransitionPriority);
 
    try {
      // Thực hiện callback (ví dụ: setState(...))
      callback();
    } finally {
      // Khôi phục priority cũ
      setUpdatePriority(prevPriority);
    }
 
    // Khi React commit xong transition update → tự clear pending
    schedulePostCommit(() => setPending(false));
  };
 
  return [isPending, startTransition];
}

Tìm hiểu về pseudo-code trên ta thấy

  • React sẽ đánh dấu update này có priority thấp hơn
  • Không render ngay lập tức, mà đợi lúc rảnh (idle hoặc sau urgent update)
  • Khi bắt đầu render transition React bật cờ isPending
  • Khi render xong, React commit vào DOM và set lại cờ isPending

NOTE

React không thật sự chạy song song code, mà chạy theo mức ưu tiên khác nhau trong cùng event loop.

Liên hệ với React Fiber

React Fiber là cây cấu trúc nơi mỗi update được gán priority (lane), useTransition thực ra gán update của callback vào một transition lane riêng biệt. Và Scheduler sẽ render lane urgent trước khi render lane transition.