useTransition та useDeferredValue в React
Конкурентні можливості в React 18+
React 18 представив конкурентне рендеринг з двома основними хуками: useTransition та useDeferredValue. Вони дозволяють вам позначати певні оновлення стану як не термінові, зберігаючи інтерфейс користувача чутливим.
useTransition
useTransition дозволяє вам позначити оновлення стану як перехід — не термінове оновлення, яке може бути перервано більш терміновими оновленнями (наприклад, введенням користувача).
import { useState, useTransition } from "react";
function SearchPage() {
const [query, setQuery] = useState("");
const [results, setResults] = useState<string[]>([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
// ✅ Термінове: оновити введення негайно
setQuery(value);
// ✅ Не термінове: оновити результати в переході
startTransition(() => {
const filtered = heavyFilter(value); // витратна операція
setResults(filtered);
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<ResultList results={results} />
</div>
);
}Як працює useTransition
isPending— булевий прапор,trueпід час виконання переходуstartTransition(callback)— обгортає не термінове оновлення стану- React може перервати перехід, якщо надходить більш термінове оновлення
- Введення залишається чутливим, оскільки важке оновлення відкладене
useDeferredValue
useDeferredValue створює відкладену копію значення, яка "відстає" від оригіналу. React оновлює його лише тоді, коли має вільну потужність.
import { useState, useDeferredValue, useMemo } from "react";
function SearchResults({ query }: { query: string }) {
// Створює відкладену версію запиту
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
// Перераховує лише тоді, коли deferredQuery змінюється
const results = useMemo(
() => heavySearch(deferredQuery),
[deferredQuery]
);
return (
<div style={{ opacity: isStale ? 0.5 : 1 }}>
{results.map(r => <div key={r.id}>{r.title}</div>)}
</div>
);
}
function App() {
const [query, setQuery] = useState("");
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<SearchResults query={query} />
</div>
);
}useTransition vs useDeferredValue
| Особливість | useTransition | useDeferredValue |
|---|---|---|
| Що відкладено | Оновлення стану (setState) | Значення |
| Де використовувати | Коли ви володієте сеттером стану | Коли у вас лише значення (пропси) |
| Повертає | [isPending, startTransition] | Відкладене значення |
| Індикатор очікування | Вбудований isPending | Порівняти оригінал з відкладеним |
Коли використовувати який
// useTransition — ви контролюєте setState
const [isPending, startTransition] = useTransition();
startTransition(() => {
setFilteredList(expensiveFilter(items));
});
// useDeferredValue — ви отримуєте значення від батька
function ChildComponent({ searchTerm }: { searchTerm: string }) {
const deferredTerm = useDeferredValue(searchTerm);
// Використовуйте deferredTerm для витратного рендерингу
}Реальні випадки використання
Перемикання вкладок
function TabContainer() {
const [tab, setTab] = useState("home");
const [isPending, startTransition] = useTransition();
function selectTab(nextTab: string) {
startTransition(() => {
setTab(nextTab); // Важкий контент вкладки може рендеритися у фоновому режимі
});
}
return (
<div>
<TabButtons onSelect={selectTab} activeTab={tab} />
{isPending ? <Spinner /> : <TabContent tab={tab} />}
</div>
);
}Фільтрація великого списку
function FilterableList({ items }: { items: Item[] }) {
const [filter, setFilter] = useState("");
const deferredFilter = useDeferredValue(filter);
const filtered = useMemo(
() => items.filter(item => item.name.includes(deferredFilter)),
[items, deferredFilter]
);
return (
<>
<input value={filter} onChange={e => setFilter(e.target.value)} />
<ul style={{ opacity: filter !== deferredFilter ? 0.7 : 1 }}>
{filtered.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</>
);
}Важливо:
useTransition та useDeferredValue — це функції React 18+ для збереження чутливості інтерфейсу під час витратних оновлень. Використовуйте useTransition, коли ви контролюєте сеттер стану, і useDeferredValue, коли ви отримуєте значення як пропс. Обидва стосуються пріоритизації термінових оновлень (введення користувача) над не терміновими (результати пошуку, важкі рендери).
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.