React 19.2 SSR Primitives: пояснення Activity, cacheSignal та PPR
Дізнайтеся, як новий компонент Activity, функція cacheSignal та часткове попереднє відтворення у React 19.2 надають розробникам прямий контроль над продуктивністю серверного відтворення.
Робота над продуктивністю у React зазвичай поділяється на дві категорії: прискорення та оптимізація початкової обробки на сервері, а також запобігання зайвій обробці на стороні клієнта після його завантаження. У цій статті кожна з частин розглядає проблему з протилежних боків, але обидві ґрунтуються на одній і тій самій філософії — швидкість досягається шляхом чіткого вказування React, яка робота є важливою, що можна відкласти та що зовсім не повинно виконуватися, а не шляхом додавання більшого кешування чи обладнання. Перша частина присвятована обробці на сервері та новим примітивам, які з’явилися у React 19.2; друга — повсякденним технікам на стороні клієнта, які забезпечують швидку реакцію завантаженого додатка.
Переосмислення продуктивності серверної обробки в React 19.2
Більшість порад щодо „продуктивності SSR“ зводяться до додавання шарів кешування та сподівань на краще. React 19.2, випущений у жовтні 2025 року, натомість надає спеціалізовані інструменти для безпосереднього керування роботою сервера. Розглядати це оновлення як незначну поправку означає пропустити справжнє підвищення швидкості — саме деталі, наведені нижче, справді впливають на результати.
Зберігайте компоненти активними, а не знищуйте їх
Частою проблемою у додатках SSR є те, що зміна вкладок, відкриття модалних вікон та переходи між маршрутами повністю знищують компоненти, стираючи їхній стан та змушуючи знову завантажувати дані. Новий компонент Activity створений саме для вирішення цієї проблеми.
// old way — full unmount, state gone, effects re-run
{activeTab === 'analytics' && <AnalyticsPanel />}
// React 19.2 — stays alive, just deprioritized
<Activity mode={activeTab === 'analytics' ? 'visible' : 'hidden'}>
<AnalyticsPanel />
</Activity>
Зберігаючи прихований вміст у активному стані замість того, щоб його видаляти, React може завантажити заздалегідь вміст прихованого блоку Activity, перш ніж користувач на нього натисне. Цей підхід дозволяє значно скоротити відчутну затримку навігації на панелях керування з інтенсивним використанням SSR, оскільки не відбувається повторного завантаження та зміни лейауту, коли вміст стає видимим.
Дозвольте cacheSignal автоматично очищувати незавершені операції
До версії 19.2, якщо серверне формування вмісту було перерване — наприклад, користувач відійшов або запит закінчився тайм-аутом — будь-які поточні запити та дані у кеші не мали способу дізнатися, що цей вміст більше не потрібен. cacheSignal вирішує цю проблему, надаючи компонентам React Server Components справжній сигнал життєвого циклу для автоматичного очищення.
async function getUserOrders(userId, { signal }) {
const res = await fetch(`/api/orders/${userId}`, { signal });
return res.json();
}
Як тільки закінчується термін життя кешу, відповідний сигнал ініціює завершення роботи, що запобігає продовженню обробки залишених запитів, які могли б споживати CPU на сервері під час піку навантаження.
Створіть статичну оболонку та передавайте решту даних у потоці
Часткова попередня обробка (PPR) є основною функцією SSR у цьому випуску. Ідея полягає у створенні статичної оболонки сторінки — навігації, макету, футера — один раз, її надсиланні безпосередньо з CDN та передачі динамічних елементів у потоці за межами механізму Suspense.
<Suspense fallback={<ProductSkeleton />}>
<PartialPreRender>
<PersonalizedRecommendations userId={user.id} />
</PartialPreRender>
</Suspense>
Об’єднайте кілька операцій розкриття Suspense в одну
Раніше, коли кілька меж Suspense вирішувалися приблизно в один і той самий момент, інтерфейс міг проявляти ефект „попкорну“, коли фрагменти контенту з’являлися один за одним, а не разом. React 19.2 групує ці процеси, щоб поведінка клієнта та сервера залишалася послідовною, а також додає підтримку Web Streams у Node.js для команд, які потребують більш детального контролю над стрімуванням.
Ці API піднімають стандарти, а не знижують їх
Ніщо з цього не може замінити основи. Вам все одно потрібно усунути патерни отримання даних типу N+1 та розділити монолітичні пакети, перш ніж ці функції зможуть вам якось допомогти. React 19.2 не зменшує мінімальної кількості необхідних оптимізаційних дій — він підвищує верхню межу швидкості добре оптимізованого додатку. Також варто прочитати офіційну інформацію про випуск React 19.2, а також параметри за замовчуванням Turbopack, введені в Next.js 16, які добре поєднуються з PPR.
Станом на 2026 рік продуктивність SSR полягає не у більш агресивному кешуванні — а у тому, щоб сказати React, що може чекати, що може передаватися потоком та що може граційно зламатися. React 19.2 нарешті надає вам необхідні інструменти для цього.
Окрім цих механізмів, специфічних для SSR, багато з того, що робить додаток на React швидким, зводиться до щоденних звичок, які залишаються актуальними незалежно від стратегії відображення чи версії React. Якщо попередній розділ був присвятований стрімінгу, попередньому відображенню та Suspense на рівні сервера, то надалі йтиметься про патерни з боку клієнта, які на практиці забезпечують високу реактивність будь-якого додатку на React.
Практичні техніки для покращення продуктивності React у повсякденному використанні
React за замовчуванням вже працює ефективно. Однак у міру зростання додатку починають накопичуватися непотрібні переробки, занадто великі списки, надмірний обсяг JavaScript та занадто багато мережевих запитів. Щоб вирішити цю проблему, рідко потрібні складні хитрощі — кілька дисциплінованих звичок зазвичай достатньо, щоб досягти бажаного результату.
Відображайте лише те, що дійсно потребує оновлення
Кожна переробка відображення змушує React знову виконати тіло функції компонента. Це саме по собі не є проблемою — справжньою витратою є повторне виконання дорогих операцій, коли насправді нічого значущого не змінилося. Уникайте розміщення непов’язаних даних стану всередині компонента, який відображає велику частину інтерфейсу, адже оновлення цього стану змушує переробити весь піддрівень разом із ним. Краще розділяйте компоненти, щоб оновлення впливало лише на ту частину інтерфейсу, яку воно справді має змінити. Метою не є повна відсутність переробок — це усунення зайвих.
Розміщуйте стан там, де він справді потрібен
Не намагайтеся піднімати кожен елемент стану на вершину дерева компонентів. Якщо лише один компонент читає певне значення, саме там воно і має знаходитися.
function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
);
}
Зберігання стану локально у такий спосіб обмежує радіус поширення оновлень, що зменшує кількість випадкових переробок інтерфейсу в інших частинах структури. Як загальне правило, розміщуйте стан ближче до компонента, який ним користується, а не вище у ієрархії.
Отримуйте значення замість їх зберігання
Не все підходить для зберігання у useState. Якщо ви вже відстежуєте firstName та lastName, немає причин окремо зберігати fullName. Неефективна версія виглядає так:
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
Простіший підхід — обчислювати значення безпосередньо під час відображення:
const fullName = `${firstName} ${lastName}`;
Це усуває як додаткову змінну стану, так і непотрібний Effect. Загалом, якщо значення можна обчислити під час відображення, ймовірно, йому не потрібен стан.
Обробка великих списків без перевантаження DOM
Обробка тисяч вузлів одночасно швидко стає дорогою. Уявіть екран чату з 10 000 повідомлень — немає потреби, щоб усі вони існували в DOM одночасно. Для великих колекцій скористайтеся одним із наступних рішень:
- Віртуалізація
- Сторінкування
- Безкінечне прокручування
Віртуалізація зберігає лише ті рядки, які наразі видимі, плюс невелику буферну зону, яка інсталюється в будь-який момент; бібліотеки на кшталт react-window реалізують цю схему замість вас. Проте не застосовуйте віртуалізацію автоматично — список з 50 елементів майже напевно не потребує її.
Відкладіть завантаження коду до моменту його необхідності
Користувачам не слід завантажувати JavaScript для функцій, які вони ще не відкрили. API React lazy та Suspense дозволяють виокремити цей код з початкового пакету:
const Settings = lazy(() => import("./Settings"));
Завдяки цьому компонент Settings завантажується лише тоді, коли він фактично відображається, а не під час початкового завантаження сторінки. Це корисно для важких або рідко використовуваних функцій — діаграм, редакторів, карт, екранів налаштувань, великих панелей керування — і зазвичай сприяє швидшому початковому завантаженню.
Зберігайте інтерактивний інтерфейс реагуючим під навантаженням
Не кожне оновлення має відбуватися одночасно з діями користувача. Поле пошуку повинно миттєво реагувати на натискання клавіш, навіть коли фільтрація великого набору даних виконується на меншій пріоритетності у фоновому режимі. Хуки React useTransition та useDeferredValue створені саме для такого компромісу. Використання механізму дебаунсингу вхідних даних дає схожий ефект:
const debouncedSearch = useDebounce(search, 500);
Замість надсилання запиту при кожному натисканні клавіши чекайте, поки користувач зробить перерву. Такі підходи корисні для полів пошуку, фільтрів, довгих списків та панелей керування з великою кількістю динамічних елементів.
Зменште кількість зайвих мережевих запитів
Швидкість відображення — це лише частина загальної картини; занадто багато активних запитів можуть зробити додаток повільним навіть тоді, коли сам процес відображення відбувається швидко. Залежно від ситуації розгляньте:
- Кешування відповідей
- Усунення дублікатів запитів
- Створення сторінок результатів
- Затримка надсилання запитів під час введення пошуку
- Скасування запитів, які більше не є актуальними
Якщо користувач швидко вводить текст
react
react performance
react performance optimization
ймовірно, ви не хочете, щоб три окремі запити працювали одночасно. Основний принцип залишається простим: уникайте змушення мережі виконувати роботу, яка вам насправді не потрібна.
Використовуйте мемоїзацію обережно
React надає три поширені примітиви мемоізації:
- useMemo зберігає у кеші результат обчислень.
- useCallback зберігає посилання на функцію між переробками.
- React.memo дозволяє пропустити переробку компонента, якщо його параметри не змінилися.
Типовий приклад:
const filteredUsers = useMemo(() => {
return users.filter(user =>
user.name.includes(search)
);
}, [users, search]);
Але мемоізація не є безкоштовною — вона має власні витрати та додає складності до навколишнього коду. Використовуйте її тоді, коли обчислення справді є ресурсоємними, коли компонент постійно переробляється без причини, або коли стабільне посилання справді має значення далі у структурі коду. Не витрачайте зусилля на оптимізацію коду, який не створює вимірюваної проблеми.
Нехай компілятор виконає частину роботи
Новим доповненням до інструментального комплексу React є React Compiler, який може автоматично застосовувати багато з цих оптимізацій замість вас — мемоїзувати значення, функції та компоненти, не вимагаючи від вас їх ручного написання у кожному випадку. Це означає, що вам більше не потрібно автоматично звертатися до:
useMemo(...)
useCallback(...)
React.memo(...)
Проте React Compiler не скасовує необхідності спочатку перевірити, чи справді існує проблема з продуктивністю. Правильна послідовність все одно полягає у підтвердженні наявності реальної проблеми, дозволі компілятору застосувати оптимізації, які він може зробити, та звертатися до ручної мемоїзації лише тоді, коли є конкретна причина.
Надайте React стабільні ідентифікатори для роботи
Ключі повідомляють React, який елемент у списку є яким саме під час кожного оновлення. Краще отримувати ключ від стабільної, унікальної властивості даних, а не від їхнього положення:
items.map(item => (
<Item key={item.id} />
));
Уникайте використання індексу масиву як ключа, оскільки переупорядкування, додавання чи видалення елементів змінює всі індекси нижче точки зміни:
items.map((item, index) => (
<Item key={index} />
));
Стабільний ключ дозволяє React правильно визначати, які елементи були додані, видалені чи оновлені, замість того щоб припускати це на основі позиції. Той самий принцип застосовується до об’єктів та функцій, які ви передаєте як пропси: створення абсолютно нового об’єкта чи функції кожного разу під час відображення скасовує сенс використання мемозованого дочірнього компонента, оскільки його пропси будуть відрізнятися щоразу, навіть якщо нічого суттєвого не змінилося.
Визначте справжню проблему перед тим, як діяти
Як тільки ви ознайомитеся з цими методами, не покладайтеся на інтуїцію для вирішення питань щодо виправлень. Відкрийте Profiler у React DevTools, щоб точно бачити, які компоненти відображаються та скільки часу це займає у кожного з них. У разі проблем, які стосуються не лише самого React, панель Performance браузера може виявити тривалі завдання, повільну виконуваність скриптів, ресурсомістку обробку макету чи перешкоди у відображенні. Замість того, щоб припускати «цей компонент працює повільно», використовуйте ці інструменти, щоб з’ясувати причину повільності.
Переконайтеся, що виправлення справді допомогло
Після застосування оптимізації знову вимірюйте показники, а не просто припускайте, що все спрацювало. Перевірьте, час відображення справді зменшився, чи став пакет меншим та чи взаємодія з програмою стала швидшою. Якщо жоден з цих показників не покращився, можливо, зміни взагалі не були необхідними.
Чек-ліст перед розгортанням
Перш ніж оприлюднити додаток на React, перевірте, чи не відображається інтерфейс, який вам не потрібен, чи знаходиться стан у правильному місці, чи не зберігаються значення, які можна було б обчислити замість цього, чи ефективно обробляються великі списки, чи важкий код завантажується лише тоді, коли це необхідно, чи пошук та взаємодія залишаються швидкими, чи не виконуються зайві запити до API, чи мемоїзація справді вирішує проблему, чи може компілятор React взяти на себе оптимізацію, чи є ваші ключі стабільними, чи ви виміряли справжню перешкоду у продуктивності та чи перевірили покращення після цього.
У кінцевому підсумку найкраща оптимізація — це не та, яка додає найбільше коду, а та, яка змушує React виконувати менше зайвої роботи.
Пов’язана література
- Розуміння React Lanes: як бітмаски кодують пріоритет оновлень — Дізнайтеся, як React використовує бітмаски для кодування кількох пріоритетів оновлень у одне ціле число, та чому для планування використовуються операції над бітами замість простих логічних флагів.
- Компілятор TypeScript на основі Go та нативна екзекуція: посібник з міграції — Дізнайтеся, як компілятор TypeScript на основі Go та нативна екзекуція в Node.js вплинуть на кодові бази React та Next.js, та що потрібно виправити у вашому tsconfig прямо зараз.