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 AppBấ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
useTransitionkhi 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