Skip to main content

Що таке "startTransition()"?

Що таке startTransition()

startTransition(callback) - повідомляє React, що оновлення стану всередині callback можуть бути відкладені (мають низький пріоритет).

Простіше кажучи:

Це спосіб сказати React: "Зроби це оновлення, але не поспішай, спочатку відрендери те, що важливе для UX (наприклад, введення тексту)."


Приклад без startTransition

javascript
function Search() { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); function handleChange(e) { const value = e.target.value; setQuery(value); // Важка фільтрація (блокує введення) const filtered = hugeList.filter(item => item.includes(value)); setResults(filtered); } return ( <> <input value={query} onChange={handleChange} /> <ul>{results.map(r => <li key={r}>{r}</li>)}</ul> </> ); }

Проблема: при швидкому введенні React повинен перерендерити величезний список на кожен символ. UI "фризиться" - курсор рухається із затримкою.


Рішення з startTransition

javascript
import { startTransition, useState } from 'react'; function Search() { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); function handleChange(e) { const value = e.target.value; setQuery(value); // термінове оновлення (введення тексту) // відкладене (низькопріоритетне) оновлення startTransition(() => { const filtered = hugeList.filter(item => item.includes(value)); setResults(filtered); }); } return ( <> <input value={query} onChange={handleChange} /> <ul>{results.map(r => <li key={r}>{r}</li>)}</ul> </> ); }

Що відбувається:

  1. React бачить, що всередині startTransition() знаходяться оновлення стану;
  2. Він ставить їх у чергу з низьким пріоритетом;
  3. Спочатку відмальовує швидкі, важливі зміни (setQuery);
  4. Потім, коли "вільний", виконує "важкі" (setResults);
  5. У результаті інтерфейс залишається відзивчивим.

Візуально

Без startTransitionЗ startTransition
Введення блокуєтьсяВведення плавне
Рендер величезного списку гальмуєReact відкладає рендер списку
UI здається "важким"UI відзивчивий і живий

1. Як працює "пріоритет" у React 18

React тепер розрізняє два типи оновлень:

Тип оновленняПрикладПріоритет
Термінові (Urgent)Введення тексту, кліки, скролВиконуються одразу
Нетермінові (Transition)Оновлення списку, фільтрація, завантаження данихВиконуються, коли React "вільний"

startTransition() позначає оновлення як "нетермінові". React може їх призупинити, скасувати або перезапустити, якщо за цей час приходять нові термінові оновлення.


2. З useTransition() - керуємо станом переходу

React також надає хук useTransition() - зручну обгортку для startTransition() з індикатором завантаження:

javascript
import { useTransition, useState } from 'react'; function Search() { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [isPending, startTransition] = useTransition(); function handleChange(e) { const value = e.target.value; setQuery(value); startTransition(() => { const filtered = hugeList.filter(item => item.includes(value)); setResults(filtered); }); } return ( <> <input value={query} onChange={handleChange} /> {isPending && <p>Оновлення результатів...</p>} <ul>{results.map(r => <li key={r}>{r}</li>)}</ul> </> ); }

useTransition() повертає:

  • isPending - true, поки перехід виконується (зручно для показу loader);
  • startTransition() - функцію для запуску низькопріоритетних оновлень.

3. Асинхронні сценарії: запити до API

javascript
import { useState, startTransition, 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 function Search() { const [query, setQuery] = useState(''); const [displayQuery, setDisplayQuery] = useState(''); function handleChange(e) { const value = e.target.value; setQuery(value); // оновлюємо "видимий" запит відкладено startTransition(() => setDisplayQuery(value)); } return ( <> <input value={query} onChange={handleChange} placeholder="Пошук..." /> <Suspense fallback={<p>Завантаження...</p>}> <SearchResults query={displayQuery} /> </Suspense> </> ); }

При швидкому введенні:

  • React оновлює поле миттєво (query);
  • відкладено змінює displayQuery і робить новий запит;
  • UI залишається відзивчивим, навіть при великій кількості даних.

4. Відмінність від useDeferredValue()

Хук / APIЩо робитьПриклад
startTransition(fn)відкладає виконання оновлень стануstartTransition(() => setList(...))
useDeferredValue(value)відкладає використання значенняconst deferred = useDeferredValue(value)

Обидва вирішують одну задачу - зробити інтерфейс плавним, але застосовуються на різних рівнях:

  • startTransition() - коли встановлюєш стан;
  • useDeferredValue() - коли використовуєш стан у компоненті.

5. Де startTransition() корисний

СценарійПеревага
Пошук по великому масивушвидке введення, фільтрація відкладена
Перемикання вкладокстара вкладка залишається, поки нова не готова
Рендер великих таблиць / списківReact не фризиться при оновленнях
Suspense + APIплавні асинхронні переходи
Оновлення графіків / фільтрівбез лагів і блокування UI

Підсумок

Що робитьЧому важливо
Позначає оновлення як "низькопріоритетні"React може їх відкласти
Зберігає відзивчивість UIшвидкі дії (введення, клік) виконуються одразу
Дозволяє плавні переходибез "фризів" і "мигання"
Працює з useTransition() і Suspenseповний контроль за async UX
Основа Concurrent RenderingReact сам вирішує, що оновити першим

Коротко:

startTransition() - це спосіб сказати React: "Це оновлення не термінове - можеш зробити його, коли буде зручно."

Він робить інтерфейс миттєво відзивчивим, особливо при важких рендерах, фільтрації та асинхронних завантаженнях.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.