Hook useDefferedValue dùng trong trường hợp ta không thể bọc bên ngoài một đoạn code tốn nhiều thời gian xử lý bằng hàm startTransition của useTransition. Đặc biệt là khi ta dùng thư viện của bên thứ ba mà không thể can thiệp.
Context
Giả sử ta có component App như sau:
import { useState } from "react"
import List from "./List"
function App() {
const [input, setInput] = useState("")
function handleChange(e) {
setInput(e.target.value)
}
return (
<div className="App">
<input type="text" value={input} onChange={handleChange} />
<List input={input} />
</div>
)
}
export default AppKhi nhập giá trị vào ô input, state của component App sẽ thay đổi và khi đó giá trị trong ô input cũng thay đổi theo. State input được truyền vào component List, component này có dạng như sau:
function List({ input }) {
const LIST_SIZE = 10000
const LargeList = () => {
const items = []
for (let i = 0; i < LIST_SIZE; i++) {
items.push(<div key={i}>{input}</div>)
}
return items
}
return <LargeList />
}
export default ListKhi input thay đổi thì largeList sẽ được tạo lại do input là props của List.
Có thể thấy, ta gặp phải vấn đề tương tự với vấn đề mà useTransition giải quyết: giá trị ở trong ô input của component App sẽ không được render ngay vì phải chờ xử lý component List.
Có thể giải quyết vấn đề này bằng hook useDeferredValue.
Implementation
Khi sử dụng hook useDeferredValue, ta sẽ tạo ra một biến “deferred” có giá trị thay đổi chỉ khi nào giá trị của biến mà nó phụ thuộc ngừng thay đổi.
Cụ thể sử dụng như sau:
import { useDeferredValue } from "react"
export default function List({ input }) {
const LIST_SIZE = 10000
const deferredInput = useDeferredValue(input)
const largeList = () => {
const items = []
for (let i = 0; i < LIST_SIZE; i++) {
items.push(<div key={i}>{deferredInput}</div>)
}
return items
}
return largeList
}Trong đoạn code trên, ta tạo ra biến deferredInput lấy giá trị từ input. Khi input đang thay đổi thì deferredInput sẽ không thay đổi. Chỉ khi nào input dừng thay đổi (có thể là trong khoảng 1 giây) thì lúc đó giá trị của input mới được cập nhật qua cho deferredInput.
Bằng cách này, ta có thể render giá trị trong ô input của component App trước mà không cần phải chờ React xử lý component List.
Để hiểu rõ hơn, ta sử dụng thêm hook useEffect như sau:
function List({ input }) {
const LIST_SIZE = 10000
const deferredInput = useDeferredValue(input)
const largeList = () => {
const items = []
for (let i = 0; i < LIST_SIZE; i++) {
items.push(<div key={i}>{deferredInput}</div>)
}
return items
}
useEffect(() => {
console.log(`Input: ${input}\nDeferred Input: ${deferredInput}`)
}, [input, deferredInput])
return largeList
}
export default ListKhi nhập vào liên tục 3 giá trị abc thì output của console sẽ là:
Input: a
Deferred Input:
Input: ab
Deferred Input:
Input: abc
Deferred Input:
Input: abc
Deferred Input: abcCó thể thấy, khi input đang thay đổi thì giá trị của deferredInput vẫn chưa thay đổi. Chỉ khi nào input dừng thay đổi thì deferredInput mới được thay đổi.