Що робить useDeferredValue() при роботі з асинхронністю?
Що робить useDeferredValue()
useDeferredValue(value)- відкладає оновлення значення, щоб React міг спочатку відрендерити швидкі й важливі частини інтерфейсу, а "важкі" чи асинхронні частини оновити трохи пізніше.
Іншими словами:
React робить UI відзивчивим, не блокуючи рендеринг повільних компонентів.
Просте пояснення
Уяви поле пошуку, де при кожному введенні символу йде запит на сервер:
<input value={query} onChange={e => setQuery(e.target.value)} />
<SearchResults query={query} />Проблема:
- користувач швидко друкує -
queryзмінюється на кожен символ; - кожен рендер запускає фільтрацію або мережевий запит;
- UI починає "лагати" (гальмувати при введенні).
1. Як вирішує useDeferredValue
import { useState, useDeferredValue, Suspense } from 'react';
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then(r => r.json());
function SearchResults({ query }) {
const { data } = useSWR(`/api/search?q=${query}`, fetcher, { suspense: true });
return <ul>{data.results.map(r => <li key={r}>{r}</li>)}</ul>;
}
export default function Search() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Пошук..."
/>
<Suspense fallback={<p>Завантаження...</p>}>
<SearchResults query={deferredQuery} />
</Suspense>
</>
);
}Що відбувається:
- Користувач швидко вводить текст (
queryоновлюється миттєво). - React не поспішає оновлювати важкий компонент (
SearchResults) для кожного символу. - Він призупиняє оновлення
deferredQuery, поки не стане "вільним". - Щойно користувач перестав активно вводити -> React оновлює
deferredQuery.
У результаті:
- поле вводу залишається відзивчивим;
- дорогі обчислення і запити не блокують UI.
2. Поведінка на практиці
| Подія | query | deferredQuery | Що робить React |
|---|---|---|---|
| Користувач набирає "React" | швидко змінюється: R -> Re -> Rea -> ... | залишається позаду | React рендерить тільки input |
| Користувач зупинився | наздоганяє query | завантажує дані для "React" |
3. Відмінність від startTransition()
| Хук | Що робить |
|---|---|
useDeferredValue(value) | відкладає використання значення (реактивна версія) |
startTransition(callback) | відкладає виконання оновлення стану |
Приклад еквівалентної поведінки:
// варіант зі startTransition
import { startTransition } from 'react';
function onChange(e) {
const value = e.target.value;
setImmediateQuery(value); // оновлюємо швидко (input)
startTransition(() => setDeferredQuery(value)); // відкладено - пошук
}useDeferredValue() робить те саме, але декларативно:
const deferredQuery = useDeferredValue(query);4. Типові кейси використання
| Кейс | Що робить useDeferredValue |
|---|---|
| Пошук | відкладає оновлення списку результатів, щоб ввід не лагав |
| Великі списки | оновлює рендер списку трохи пізніше, щоб UI залишався відзивчивим |
| Suspense-завантаження даних | дозволяє показувати старі дані, поки нові підвантажуються |
| Фільтри / сортування | не перерендерює таблицю при кожному кліку |
| Графіки і візуалізація | рендерить оновлення тільки коли React "вільний" |
5. Приклад з візуальним ефектом (висока навантаженість)
function HeavyList({ filter }) {
const items = Array.from({ length: 5000 }, (_, i) => `Елемент ${i}`);
const filtered = items.filter(i => i.toLowerCase().includes(filter.toLowerCase()));
return (
<ul>{filtered.map(i => <li key={i}>{i}</li>)}</ul>
);
}
export function Search() {
const [filter, setFilter] = useState('');
const deferredFilter = useDeferredValue(filter);
return (
<>
<input
value={filter}
onChange={e => setFilter(e.target.value)}
placeholder="Фільтр..."
/>
<HeavyList filter={deferredFilter} />
</>
);
}При швидкому введенні:
- поле вводу реагує миттєво (рендер "не блокується");
- список оновлюється з невеликою затримкою (трохи пізніше).
6. Як це працює "під капотом"
useDeferredValue:
- повертає "затриману копію" значення;
- позначає оновлення, що залежать від нього, як незначні (low priority);
- React виконує ці оновлення тільки коли UI вільний;
- якщо під час очікування приходить нове значення - старе ігнорується.
Це частина React Concurrent Rendering - нового механізму, де React сам вирішує, що оновити спочатку, а що почекає.
ПІДСУМОК
| Що робить | Навіщо потрібно |
|---|---|
| Відкладене оновлення значення | запобігає лагам при частих оновленнях |
| Не блокує ввід і швидкі оновлення | UI залишається відзивчивим |
| Дозволяє плавно працювати з Suspense | старі дані залишаються на екрані |
| Використовує низький пріоритет рендера | React робить "розумний" план оновлень |
| Чудово підходить для пошуку, фільтрів, списків | зменшує зайві запити і рендери |
Коротко:
useDeferredValue()- це спосіб сказати React: "Ось нове значення, але не поспішай з його рендером - онови, коли буде час."Він робить інтерфейс плавним і відзивчивим, особливо при асинхронному завантаженні даних або рендері важких компонентів.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.