Context

Xét component sau:

import { useState, useTransition } from "react"
 
function App() {
  const [input, setInput] = useState("")
  const [list, setList] = useState([])
 
  const LIST_SIZE = 10000
 
  function generateLargeList(inputValue) {
    const largeList = []
    for (let i = 0; i < LIST_SIZE; i++) {
      largeList.push(inputValue)
    }
    return largeList
  }
 
  function handleChange(e) {
    setInput(e.target.value)
    setList(generateLargeList(e.target.value))
  }
 
  return (
    <div className="App">
      <input type="text" value={input} onChange={handleChange} />
      {list.map((item, index) => {
        return <div key={index}>{item}</div>
      })}
    </div>
  )
}
 
export default App

Bất cứ khi nào ta nhập một giá trị v vào ô input, component sẽ thực hiện cập nhật state và sẽ render ra:

  • Giá trị v ở trong ô input.
  • Một danh sách lớn gồm 10000 giá trị v.

Important

React có cơ chế gom nhiều lần cập nhật state lại thành một lần render duy nhất. Nói cách khác, giá trị trong ô input và danh sách sẽ xuất hiện gần như là đồng thời.

Dẫn đến, khi ta nhập vào nhiều ký tự, chẳng hạn như abcd, thì giá trị trong ô input sẽ cần phải chờ hết 4 * 10000 = 40000 lần lặp rồi mới được render ra cùng lúc với danh sách 10000 giá trị.

Để giá trị trong ô input được render trước mà không cần phải chờ vòng lặp thì ta có thể sử dụng hook useTransition.

Usage

Thiết lập hook này như sau:

import { useState, useTransition } from "react"
 
export default function App() {
  const [input, setInput] = useState("")
  const [list, setList] = useState([])
  const [isPending, startTransition] = useTransition()
 
  // ...
}

Bọc tác vụ cập nhật state mà cần nhiều thời gian xử lý ở bên trong callback của hàm startTransition như sau:

function handleChange(e) {
  setInput(e.target.value)
  startTransition(() => setList(generateLargeList(e.target.value)))
}

Khi đó, React sẽ thực hiện hai lần render chứ không phải một lần như trước kia:

  • Lần render đầu tiên là giá trị ở trong ô input, đây là thành phần được ưu tiên nên nó sẽ render trước.
  • Lần render thứ hai là danh sách 10000 giá trị, đây là thành phần có độ ưu tiên thấp nên nó chỉ được render khi React không bận render những thành phần khác. Thời điểm render sẽ được React tự tính toán.

Có thể nói, hàm startTransition giúp sắp xếp lại độ ưu tiên của các thành phần khi thực hiện render. Ta gọi cơ chế này là asynchronous rendering.

Ta có thể dùng biến isPending để thực hiện Conditional Rendering khi tác vụ ở trong callback của startTransition đang được xử lý:

<div className="App">
  <input type="text" value={input} onChange={handleChange} />
  {isPending
    ? "Loading..."
    : list.map((item, index) => {
        return <div key={index}>{item}</div>
      })}
</div>

Important

Chỉ sử dụng useTransition khi ta muốn ưu tiên render một số thành phần nào đó trước mà không cần phải đợi những thành phần khác được render. sort file.ctime asc

Resources