Технології
React <Activity /> deep dive: зберігає state, ставить Effects на паузу, рендерить у фоні — практичні патерни, реальні edge cases і чекліст rollout
<Activity /> (React 19.2) — третій варіант між unmount UI та CSS-hide: він зберігає state, ховає DOM, знищує Effects і зсуває hidden-оновлення у нижчий пріоритет. Це продовження гайду про React 2026 primitives: тут — реальні патерни для tabs/drawers/shell UI, pre-render наступного екрану, SSR/hydration наслідки, пастки екосистеми та production чекліст.

React 2026 Primitives & Compiler Upgrade Guide
Фокусний розділ про <Activity />. Спочатку прочитайте огляд, далі — впроваджуйте патерни інкрементально з вимірюваними guardrails.
Усі статті в цьому гайді
01
Overview: React 2026 primitives and compiler-era mental model
Загальна картина: що змінилось і де кожен примітив реально підходить.
02
useActionState deep dive: mutation flows, optimistic UI, and integration patterns
Коли useActionState знімає boilerplate, а коли data layer все ще “власник” проблеми.
03
React <Activity />: keep state, pause Effects, background render
Практичні патерни, performance trade-offs, пастки екосистеми та чекліст rollout.
04
useEffectEvent deep dive: effect design, subscriptions, and analytics
Linter-friendly межі для ефектів без stale closures та reconnect churn.
Частина React 2026 гайду
В огляді ми пояснюємо, як Actions, <Activity />, useEffectEvent, Performance Tracks, SSR batching та Compiler складаються в один напрям (а не набір випадкових фіч).[2]
Це розділ з нашого гайду про React 2026 primitives. Якщо хочете спочатку дорожню карту — почніть з огляду:
Що ви заберете з цього розділу
Docs прямо кажуть: у hidden React ховає дітей через display: none, знищує Effects (cleanup підписок), зберігає state, і діти можуть re-render на нові props (нижчий пріоритет).[1]
<Activity /> найкраще розуміти як lifecycle + scheduling контроль для hidden UI. Цей розділ — про те, як використати його і не зробити гірше.
• Реальна семантика
mode="visible" | "hidden": DOM, state, Effects, пріоритет апдейтів.[1][2]• Патерни, які реально працюють: tabs/drawers, “warm next screen”, важкі панелі, SSR gating.[1][2]
• Як зробити hidden-роботу дешевою (контроль prop churn) і не створити випадково дорогі оновлення.[1]
• Edge cases екосистеми: що ламається в third-party віджетах і як себе захистити.[7]
• Production чекліст rollout (feature flags + метрики + memory budget).
React 19.2: hidden ховає дітей, unmount Effects, дефери апдейти і дозволяє background preparation.[2]
Навіщо <Activity />: прогалина між unmount та CSS-hide
Історично більшість апок використовували:
1) Conditional rendering: життєвий цикл чистий, але ви втрачаєте локальний state при unmount (форми, scroll, еферемний UI-state).
2) CSS hide: state зберігається, але hidden піддерева можуть тримати підписки/таймери і конкурувати за main-thread.
React 19.2 позиціонує Activity як спосіб розбити апку на “activities”, які можна контролювати і пріоритизувати. У hidden він ховає дітей, unmount Effects і дефери апдейти, доки React не залишиться нічого важливішого.[2]
Ментальна модель: що робить `hidden` (і чого не робить)
Найчастіша помилка: думати, що hidden означає “React не рендерить”. Він може re-render — але з нижчим пріоритетом.
React Labs пояснює задум: концептуально компонент “unmounted”, але React зберігає state. Вони радять <StrictMode>, щоб рано знаходити проблемні Effects — Activity буде mount/unmount Effects при перемиканні.[3]
React Reference: hidden ховає DOM, знищує Effects, дефери апдейти; visible відновлює попередній state і пересоздає Effects.[1]
Скріншот секції mental-modelAPI: базовий патерн
Мінімальне використання:
import { Activity, useState } from "react";
export function SidebarShell() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen((v) => !v)}>
{open ? "Hide" : "Show"}
</button>
<Activity mode={open ? "visible" : "hidden"}>
<Sidebar />
</Activity>
</>
);
}З reference: у hidden Effects знищуються (cleanup), діти ховаються через display: none, і можуть re-render на props з нижчим пріоритетом.[1]
Патерн 1: Tabs / drawers з state без hidden side-effects
Це “killer” кейс: зберегти state (форми, scroll), але не тримати підписки/таймери активними у hidden.[1][2]
import { Activity } from "react";
export function SettingsTabs({ tab }: { tab: "profile" | "billing" }) {
return (
<>
<Activity mode={tab === "profile" ? "visible" : "hidden"}>
<ProfileTab />
</Activity>
<Activity mode={tab === "billing" ? "visible" : "hidden"}>
<BillingTab />
</Activity>
</>
);
}Якщо вкладка потребує безперервної background-роботи для коректності — не ховайте “двигун” всередині Activity (нижче покажемо як правильно).
Патерн 2: “Warm next screen” по intent (hover/focus) без блокування форграунду
React 19.2 прямо каже: можна pre-render і продовжувати рендерити hidden частини без впливу на visible performance.[2] Трюк — почати прогрів на *intent* (hover/focus), а не на клік.
import { Activity, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
export function ContinueButton({ nextHref }: { nextHref: string }) {
const router = useRouter();
const [armed, setArmed] = useState(false);
useEffect(() => {
if (!armed) return;
router.prefetch(nextHref);
}, [armed, nextHref, router]);
return (
<>
<button onMouseEnter={() => setArmed(true)} onFocus={() => setArmed(true)}>
Continue
</button>
<Activity mode={"hidden"}>
<NextStepShell />
</Activity>
</>
);
}Цей варіант свідомо консервативний: він готує піддерево у фоні, але ніколи його не показує до навігації. Це scheduling hint, не UI-хак.
Патерн 3: Здешевіть hidden рендер — контролюйте prop churn на межі
import { Activity, useMemo } from "react";
type Filters = { key: string; q: string; sort: "new" | "top" };
export function HeavyPanelHost(props: {
visible: boolean;
filters: Filters;
liveTick: number;
}) {
const stableFilters = useMemo(() => props.filters, [props.filters.key]);
return (
<Activity mode={props.visible ? "visible" : "hidden"}>
<HeavyPanel filters={stableFilters} />
</Activity>
);
}Hidden діти можуть re-render на зміни props.[1] У реальних апках основна ціна — це не Activity, а постійно змінні props, які ви прокидуєте в hidden дерево.
• Не передавайте high-frequency значення (
now, стрімові метрики, scroll position) у hidden дерева.• Стабілізуйте props, які не мають змінюватись у hidden (memoize по stable key).
• Якщо live значення потрібні — тримайте їх у маленькому stable engine і передавайте в hidden тільки грубий snapshot.
Патерн 4: Не ховайте data engine всередині Activity
Це найпопулярніша production пастка: Activity знищує Effects у hidden.[1][2] Якщо коректність залежить від постійних підписок/полінгу — винесіть двигун назовні й передавайте дані всередину.
import { Activity, useEffect, useState } from "react";
type Live = { count: number };
declare function subscribeLive(onValue: (v: Live) => void): () => void;
function LiveEngine({ onValue }: { onValue: (v: Live) => void }) {
useEffect(() => subscribeLive(onValue), [onValue]);
return null;
}
export function ScreenWithPanel({ open }: { open: boolean }) {
const [live, setLive] = useState<Live>({ count: 0 });
return (
<>
<LiveEngine onValue={setLive} />
<Activity mode={open ? "visible" : "hidden"}>
<Panel live={live} />
</Activity>
</>
);
}Цей дизайн робить коректність незалежною від видимості панелі й прибирає баги типу “в hidden перестало оновлюватись”.
SSR / hydration наслідки (тут Activity стає архітектурним інструментом)
Activity — це не лише про state retention. Це також SSR-важіль: що входить у початковий HTML, а що ви свідомо відкладете.
Практичний SSR gating приклад:
import { Activity, useState } from "react";
export function Page() {
const [advanced, setAdvanced] = useState(false);
return (
<>
<PrimaryContent />
<button onClick={() => setAdvanced((v) => !v)}>Advanced</button>
<Activity mode={advanced ? "visible" : "hidden"}>
<AdvancedPanel />
</Activity>
</>
);
}SSR response
Reference каже: UI всередині mode="hidden" не включається в SSR response.[1]
Hydration
Reference описує наслідки scheduling/hydration навколо visibility та пріоритизації.[1]
Use case
Тримайте truly non-critical UI поза initial HTML, щоб зменшити payload і покращити perceived load.
ViewTransition: show/hide Activity може тригерити анімації
Reference має caveat: коли Activity всередині ViewTransition, show/hide може активувати enter/exit анімації залежно від того, як виконано апдейт.[1]
Якщо ви будуєте navigation shell з transitions — тестуйте Activity toggles окремо під startTransition і вашим патерном ViewTransition (таймінг і межі анімації мають значення).
Реальні edge cases екосистеми: що ламається і як захищатись
Якщо third-party компонент тримає інтерактивний state всередині, але реініціалізується при mount, Activity toggles це проявлять. Тоді зазвичай потрібне: (a) винести state назовні (controlled props), (b) оновити/патчити бібліотеку, або (c) не обгортати цей сабтрі Activity.
Найшвидший спосіб втратити довіру до нового примітиву — загнати його в third-party компонент, який не розрахований на такі lifecycle-семантики.
• deck.gl: issue, де приховування <DeckGL> через Activity скидає view state після повторного показу.[7]
• Prior art keep-alive допомагає думати про eviction, cache budgets і “що саме варто зберігати”.[8]
• Безпечний rollout: стартуйте з owned UI (таби/дровери/форми), а потім — third-party heavy widgets тільки після тестів на відновлення state.
Production rollout чекліст (як ми це робимо на клієнтських проєктах)
Швидка KPI табличка для PR опису:
| Metric | Baseline | After Activity | Notes |
|---|---:|---:|---|
| INP (p75) | | | toggle tabs/drawer |
| Long tasks > 50ms | | | navigation + warmup |
| JS heap after 5 toggles | | | memory budget check |
| CPU time during toggle | | | prop churn detection |Activity має сенс, якщо він покращує UX без прихованих регресій по памʼяті/CPU. Сприймайте як rollout, а не як “рефактор”.
• Feature flag на нову межу + поступовий rollout (per route / per segment).
• Міряйте: INP, long tasks, memory growth (особливо якщо багато hidden activities).
• У dev/staging увімкніть
<StrictMode>, щоб зловити неправильні припущення про Effects.[3]• Задайте memory budget: скільки hidden “screens” дозволяєте одночасно.
• Тестуйте Activity toggles з: Suspense boundaries, navigation transitions і third-party віджетами.
Часті запитання
Ні. Reference прямо каже, що діти можуть re-render у відповідь на нові props, але з нижчим пріоритетом за visible контент.[1]
Ні. Коли Activity стає hidden, React знищує Effects (виконує cleanup) і монтує їх знову, коли Activity стає visible.[1][2]
Ні. Віртуалізація зменшує DOM/рендер для великих списків. Activity — про lifecycle/scheduling для цілих UI-піддерев: табів, дроверів, екранів.[1][2]
Так, як свідомий важіль: reference каже, що UI всередині `mode="hidden"` не включається в SSR response, що може зменшити initial HTML для non-critical панелей.[1]
Сховати піддерево, яке містить “data engine” через Effects (підписки/полінг). Hidden знищує Effects, тож коректність ламається, якщо двигун не винести назовні межі Activity.[1][2]
Він відвантажений у React 19.2 і має документовану поведінку та офіційний reference.[1][2] React Labs радить StrictMode під час adoption, щоб зловити неочікувані side effects, коли Effects монтуються/демонтуються через Activity toggles.[3]
Джерела
• React Reference: <Activity> (DOM hiding, Effects cleanup, low-priority re-renders, SSR notes) Читати джерело ↗
• React 19.2 (official): Activity semantics and goals Читати джерело ↗
• React Labs: View Transitions, Activity, and more (StrictMode guidance; concept: unmounted but state preserved) Читати джерело ↗
• React Learn: Preserving and Resetting State (baseline mental model for conditional rendering) Читати джерело ↗
• LogRocket: React 19.2 is here (Activity + useEffectEvent overview and practical framing) Читати джерело ↗
• This Week In React #252 (context roundup: React 19.2, Activity, useEffectEvent) Читати джерело ↗
• deck.gl issue: Activity compatibility bug (state resets after hide/show) Читати джерело ↗
• Prior art: React keep-alive patterns (state preservation, caching, eviction strategies) Читати джерело ↗
Хочете впровадити Activity без регресій?
PAS7 Studio допомагає безпечно впроваджувати React 19.2 primitives: Activity, Suspense, transitions і Next.js prefetch патерни — з вимірюваними метриками до/після.
Отримаєте конкретний rollout план: де Activity допомагає, де шкодить, і як тримати памʼять + CPU під контролем.
React <Activity />: keep state, pause Effects, background render