PAS7 Studio

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 чекліст.

24 лют. 2026 р.· 7 хв читання· Технології
Превʼю React <Activity /> на фоні компʼютерного заліза: “State kept. Effects paused.”

В огляді ми пояснюємо, як 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]

Скріншот секції overview

Історично більшість апок використовували:

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 означає “React не рендерить”. Він може re-render — але з нижчим пріоритетом.

React Labs пояснює задум: концептуально компонент “unmounted”, але React зберігає state. Вони радять <StrictMode>, щоб рано знаходити проблемні Effects — Activity буде mount/unmount Effects при перемиканні.[3]

hidden

React ховає дітей через display: none у hidden.[1]

збережений

React зберігає state, щоб UI швидко відновився у visible.[1]

знищені

Effects cleanup у hidden; при visible — монтуються знову.[1][2]

deprioritized

Hidden діти можуть re-render на props з нижчим пріоритетом.[1]

React Reference: hidden ховає DOM, знищує Effects, дефери апдейти; visible відновлює попередній state і пересоздає Effects.[1]

Скріншот секції mental-model

Мінімальне використання:

TSX
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]

Це “killer” кейс: зберегти state (форми, scroll), але не тримати підписки/таймери активними у hidden.[1][2]

TSX
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 (нижче покажемо як правильно).

React 19.2 прямо каже: можна pre-render і продовжувати рендерити hidden частини без впливу на visible performance.[2] Трюк — почати прогрів на *intent* (hover/focus), а не на клік.

TSX
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-хак.

TSX
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.

Це найпопулярніша production пастка: Activity знищує Effects у hidden.[1][2] Якщо коректність залежить від постійних підписок/полінгу — винесіть двигун назовні й передавайте дані всередину.

TSX
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 перестало оновлюватись”.

Activity — це не лише про state retention. Це також SSR-важіль: що входить у початковий HTML, а що ви свідомо відкладете.

Практичний SSR gating приклад:

TSX
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>
    </>
  );
}

hidden не входить

Reference каже: UI всередині mode="hidden" не включається в SSR response.[1]

важливий пріоритет

Reference описує наслідки scheduling/hydration навколо visibility та пріоритизації.[1]

advanced panels

Тримайте truly non-critical UI поза initial HTML, щоб зменшити payload і покращити perceived load.

Reference має caveat: коли Activity всередині ViewTransition, show/hide може активувати enter/exit анімації залежно від того, як виконано апдейт.[1]

Якщо ви будуєте navigation shell з transitions — тестуйте Activity toggles окремо під startTransition і вашим патерном ViewTransition (таймінг і межі анімації мають значення).

Якщо 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.

Швидка KPI табличка для PR опису:

MD
| 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 віджетами.

Чи означає `hidden`, що React взагалі не рендерить піддерево?

Ні. Reference прямо каже, що діти можуть re-render у відповідь на нові props, але з нижчим пріоритетом за visible контент.[1]

Чи будуть працювати підписки/таймери, поки Activity hidden?

Ні. Коли Activity стає hidden, React знищує Effects (виконує cleanup) і монтує їх знову, коли Activity стає visible.[1][2]

Чи замінює Activity віртуалізацію списків (react-window, virtuoso)?

Ні. Віртуалізація зменшує DOM/рендер для великих списків. Activity — про lifecycle/scheduling для цілих UI-піддерев: табів, дроверів, екранів.[1][2]

Чи може Activity допомогти з SSR перформансом?

Так, як свідомий важіль: reference каже, що UI всередині `mode="hidden"` не включається в SSR response, що може зменшити initial HTML для non-critical панелей.[1]

Яка найбільша production пастка з Activity?

Сховати піддерево, яке містить “data engine” через Effects (підписки/полінг). Hidden знищує Effects, тож коректність ламається, якщо двигун не винести назовні межі Activity.[1][2]

Чи достатньо стабільний Activity для production?

Він відвантажений у React 19.2 і має документовану поведінку та офіційний reference.[1][2] React Labs радить StrictMode під час adoption, щоб зловити неочікувані side effects, коли Effects монтуються/демонтуються через Activity toggles.[3]

PAS7 Studio допомагає безпечно впроваджувати React 19.2 primitives: Activity, Suspense, transitions і Next.js prefetch патерни — з вимірюваними метриками до/після.

Отримаєте конкретний rollout план: де Activity допомагає, де шкодить, і як тримати памʼять + CPU під контролем.

Ви тут03/04

React <Activity />: зберігайте state, ставте Effects на паузу і рендерьте у фоні

Пов'язані статті

ai-assistants

Скільки коштує розробка AI асистента у 2026: RAG чатбот, база знань, CRM, Telegram та підтримка

Практичний гід для бізнесу: від чого залежить ціна розробки AI асистента у 2026 році, що входить у RAG чатбот, інтеграції з CRM, Telegram, guardrails, оцінювання, моніторинг і супровід.

blogs

AI-assisted attacks і prompt injection у 2026: нова поверхня атак для продуктів з AI

Практичний гайд з безпеки AI-функцій: prompt injection, tool abuse, excessive agency, RAG poisoning, data leakage, output handling, human approval gates і permissions для AI agents.

blogs

AI для розробки лендінгів: де він реально прискорює запуск, а де псує конверсію

Дослідження про використання AI у розробці лендінгів: v0, Webflow AI, Builder.io, Framer-подібні AI builders, генерація UX, copy, SEO, персоналізація, A/B тести, ризики шаблонності, безпеки, доступності та технічного боргу.

growth

AI SEO / GEO у 2026: ваші наступні клієнти — не люди, а агенти

Пошук зміщується від кліків до відповідей. Боти та AI-агенти сканують, цитують, рекомендують і дедалі частіше купують. Дізнайтесь, що таке AI SEO / GEO, чому класичного SEO вже недостатньо, і як PAS7 Studio допомагає брендам перемагати у «агентному» вебі.

Професійна розробка для вашого бізнесу

Створюємо сучасні веб-рішення та боти для бізнесу. Дізнайтеся, як ми можемо допомогти вам досягти цілей.