Skip to content

Условный рендеринг

Простыми словами

Как показать или спрятать кусок страницы по условию — и почему это компоненты (Show, Dynamic), а не просто тернарник.

Значения в JSX — привязки: они патчат текст и атрибуты на месте. Когда должна измениться структура DOM, объявляется динамический регион.

<Show>

tsx
import { Show } from "@continuum-js/dom";

<Show when={user} fallback={() => <Login />}>
  {(u) => <Profile user={u} />}
</Show>;

when принимает State<T | null | undefined | false>. Поддерево перестраивается только при смене истинности — обновления уже-истинного значения ничего не перестраивают (ребёнок получает значение на момент переключения; для живых частей передавайте внутрь States).

Функциональная форма для кода без JSX — when(cond, then, else?) с State<boolean>.

<Dynamic> — переключение по значению

Когда веток больше двух, регион ключуется самим значением:

tsx
import { Dynamic } from "@continuum-js/dom";

<Dynamic value={state}>
  {(s) =>
    s.status === "loading" ? (
      <Spinner />
    ) : s.status === "error" ? (
      <ErrorBox error={s.error} />
    ) : s.status === "ok" ? (
      <Profile user={s.value} />
    ) : null
  }
</Dynamic>;

Регион перестраивается при изменении value (Object.is). Чтобы не перестраивать по несущественным изменениям, сузьте State и уберите дубликаты:

ts
import { dedupe } from "@continuum-js/std";

const status = dedupe(state.map((s) => s.status));

dyn — примитив

Show и Dynamic — сахар над dyn(state, render): регионом между комментариями-маркерами, который при смене значения уничтожает старое поддерево и строит новое. Берите dyn напрямую, когда пишете собственный control-flow-компонент — Outlet роутера устроен ровно так, с ключом по идентичности роута.

<Portal>

Структура, которая рендерится в другом месте (модалки, тултипы), но принадлежит времени жизни компонента:

tsx
<Portal mount={document.body}>
  <div class="modal">…</div>
</Portal>

Содержимое удаляется и уничтожается вместе с владеющим поддеревом, где бы оно ни было смонтировано.

<Catch> — граница ошибок

Исключение при построении куска страницы не должно убивать всё приложение. <Catch> показывает вместо упавшего поддерева fallback и даёт повторить:

tsx
import { Catch } from "@continuum-js/dom";

<Catch
  fallback={(error, reset) => (
    <div class="error">
      Что-то сломалось: {String(error)}
      <button onClick={reset}>Попробовать снова</button>
    </div>
  )}
>
  {() => <RiskyWidget />}
</Catch>;
  • Дети — обязательно thunk ({() => …}): жадный JSX выполнился бы (и упал) до того, как Catch получит управление.
  • Ловится: исключение при построении детей и исключение при пересборке любого вложенного Show/Dynamic/dyn внутри границы. Владение упавшего поддерева уничтожается — никаких утёкших подписок и таймеров.
  • reset строит детей заново с нуля; исключение в самом fallback'е эскалирует к внешней границе.
  • Не ловится: исключения внутри map-функций привязок (держите их чистыми). Ошибки асинхронщины вообще не летят исключениями — perform и resource отдают их данными (Result, Async), которые рендерятся как любые значения.

Незнакомый термин? Вся терминология этой документации объяснена в глоссарии.