Що робить useDeferredValue()
Що робить useDeferredValue()
useDeferredValue(value) повертає «відкладену версію» значення.
Якщо value часто змінюється (наприклад, при введенні в інпут), React може не одразу прокинути нове значення у важкі ділянки UI (списки, графіки), щоб інтерфейс лишався відгукливим. Ввід реагує миттєво, а «дорогий» рендер підтягується трохи пізніше.
Міні-приклад:
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query); // "повільна" копія
// Важкий список фільтрується за deferredQuery, а не за миттєвим query
const filtered = useMemo(() => heavyFilter(items, deferredQuery), [items, deferredQuery]);Поки користувач друкує, query змінюється одразу (інпут не лагає), а deferredQuery оновиться трохи пізніше - важкий рендер не «сіпається» на кожен символ.
Чим useDeferredValue() схожий на debounce
Обидва прийоми згладжують часті зміни, щоб не викликати важку роботу занадто часто.
Схожість:
- Знижують частоту «дорогих» переобчислень/рендерів при швидкому вводі.
- Користувач бачить плавний UI без лагів.
Чим useDeferredValue() відрізняється від debounce
Головні відмінності - у рівні і семантиці:
| Критерій | useDeferredValue() | Debounce (наприклад, debounce(fn, 300)) |
|---|---|---|
| Де працює | Всередині React, розуміє пріоритети, інтегрований з Concurrent Rendering | Поза React (утиліта часу), «тупо» затримує виклики |
| Що відкладає | Прокидання значення в споживачів; рендер можна перервати/поновити | Виклик функції за таймером після паузи |
| Керування затримкою | Динамічне, без жорсткого таймера; React сам вирішує, коли «наздогнати» | Жорсткий інтервал (наприклад, 300 мс) |
| Скасування/переривання | Так: рендер можна перервати, якщо прийшло важливіше оновлення | Так: скидання таймера при новому вводі, але без знань про пріоритети React |
| SSR/гідратація/конкурентність | Природно сумісний | Ніяк не інтегрований |
| Типові кейси | Розділити «швидкий» ввід і «важкий» UI | Мережа/обробники, обмеження частоти викликів, autocomplete API |
Підсумок: debounce - про час, useDeferredValue - про пріоритети рендеру.
Чим useDeferredValue() відрізняється від startTransition()
Обидва механізми служать для «нетермінових» оновлень, але точка застосування різна:
| Критерій | useDeferredValue(value) | startTransition(() => setState(...)) |
|---|---|---|
| Рівень | Значення: віддає «повільну» копію часто змінюваного value | Операція/оновлення: позначає конкретні setState як низькопріоритетні |
| Хто ініціює | Споживач значення обирає «повільну» версію | Ти сам позначаєш частину оновлень як transition |
| Контроль статусу | Немає вбудованого isPending | Через useTransition() отримуєш isPending |
| Гранулярність | Пасивна: будь-яке місце, де використовуєш deferred value, буде «наздоганяти» пізніше | Активна: ти явно обгортаєш «важкі» оновлення в transition |
| Типові кейси | Важкі мемо-обчислення/списки, що залежать від швидко змінюваного value | Навігація, фільтрація, запити, масштабні перебудови дерева |
| Асинхронність (React 19+) | Не про async | startTransition можна await-нути (очікувати async роботу) |
Коротко:
useDeferredValue- «дай мені версію цього значення, яку можна оновити пізніше».startTransition- «оці оновлення стану зроби низькопріоритетними».
Коли що обирати
- Важкий список/графік реагує на швидкий ввід → почни з
useDeferredValue(query). Просто і майже без коду. - Потрібно позначити пакет оновлень як «нетерміновий» і показати статус «триває оновлення» →
const [isPending, startTransition] = useTransition(); startTransition(() => setState(...)). - Обмежити частоту мережевих викликів / обробників вводу → debounce (мережевий шар/утиліти). Можна комбінувати: debounce на запит,
useDeferredValue- для важкого UI.
Часті патерни
Комбо «інпут + deferred фільтрація»:
const [query, setQuery] = useState('');
const deferred = useDeferredValue(query);
const list = useMemo(() => heavyFilter(items, deferred), [items, deferred]);Позначити перебудову як перехідну (з індикатором):
const [isPending, startTransition] = useTransition();
const onTabChange = (tab: string) => {
startTransition(() => setActiveTab(tab)); // велика перебудова дочірніх
};
return <>
<Tabs onChange={onTabChange} />
{isPending && <Skeleton />}
<HeavyPane tab={activeTab} />
</>;Debounce тільки для запиту:
const debouncedFetch = useMemo(() => debounce(fetchUsers, 300), []);
useEffect(() => {
debouncedFetch(query);
}, [query, debouncedFetch]);(і паралельно можна використати useDeferredValue(query) для «плавного» UI)
Вижимка
useDeferredValue- відкладена версія значення. Про пріоритети, не про таймер.- Debounce - таймер: відкладає виклики функцій після паузи.
startTransition- позначення оновлень стану як низькопріоритетних, зisPendingі (в React 19+)await.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.