8. Control Flow: Show, For, Switch, Index
Solid предоставляет встроенные компоненты управления потоком вместо операторов JavaScript. Это не синтаксический сахар — это оптимизация. <For> не пересоздаёт DOM-узлы при сортировке, <Show> не монтирует/демонтирует при каждом рендере без необходимости.
Почему компоненты, а не тернарники?
Заголовок раздела «Почему компоненты, а не тернарники?»В React {condition ? <A /> : <B />} — норма. В Solid так тоже можно, но это хуже:
// React-стиль — работает, но неоптимальноfunction Bad() { const [show, setShow] = createSignal(true); return <div>{show() ? <Heavy /> : <Light />}</div>;}
// Solid-стиль — Show отслеживает только изменение условияfunction Good() { const [show, setShow] = createSignal(true); return ( <Show when={show()} fallback={<Light />}> <Heavy /> </Show> );}<Show> — условный рендеринг
Заголовок раздела «<Show> — условный рендеринг»import { Show } from 'solid-js';
function Profile(props: { user: User | null }) { return ( <Show when={props.user} fallback={<p>Загрузка...</p>} > {/* when передаётся как аргумент — без повторной проверки */} {(user) => <h1>Привет, {user().name}!</h1>} </Show> );}Форма с колбэком {(user) => ...} — это «keyed Show»: user() — мемоизированный аксессор к значению when, TypeScript знает, что оно не null/undefined.
<For> — списки с ключами
Заголовок раздела «<For> — списки с ключами»import { For } from 'solid-js';
interface Todo { id: number; text: string; done: boolean;}
function TodoList(props: { todos: Todo[] }) { return ( <ul> <For each={props.todos} fallback={<li>Список пуст</li>}> {(todo, index) => ( <li> <span>{index() + 1}. {todo.text}</span> <Show when={todo.done}>✅</Show> </li> )} </For> </ul> );}For отслеживает изменения по ссылочному равенству объектов. Если объект тот же — DOM-узел не пересоздаётся, даже если массив перетасован.
<Index> — стабильные индексы
Заголовок раздела «<Index> — стабильные индексы»import { Index } from 'solid-js';
// Index — для примитивных массивов или когда важна позицияfunction NumberList(props: { nums: number[] }) { return ( <ul> <Index each={props.nums}> {/* item — сигнал, index — стабильное число */} {(item, index) => ( <li>Позиция {index}: {item()}</li> )} </Index> </ul> );}For | Index | |
|---|---|---|
| Стабильность | Стабильный DOM по объекту | Стабильный DOM по индексу |
item тип | Значение (T) | Accessor (T) — сигнал |
index тип | Accessor (number) — сигнал | Число |
| Лучше для | Объектов с id | Примитивов, матриц |
<Switch> и <Match> — множественные условия
Заголовок раздела «<Switch> и <Match> — множественные условия»import { Switch, Match } from 'solid-js';
type Status = 'loading' | 'error' | 'empty' | 'data';
function DataView(props: { status: Status; data?: string }) { return ( <Switch fallback={<p>Неизвестный статус</p>}> <Match when={props.status === 'loading'}> <Spinner /> </Match> <Match when={props.status === 'error'}> <ErrorMessage /> </Match> <Match when={props.status === 'empty'}> <EmptyState /> </Match> <Match when={props.status === 'data'}> <DataDisplay data={props.data!} /> </Match> </Switch> );}<Dynamic> — динамический компонент
Заголовок раздела «<Dynamic> — динамический компонент»import { Dynamic } from 'solid-js/web';
const components = { card: CardView, list: ListView, table: TableView,};
function LayoutSwitcher(props: { layout: keyof typeof components }) { return ( <Dynamic component={components[props.layout]} data={someData} /> );}
// Или для HTML-тегов:function Heading(props: { level: 1 | 2 | 3; children: any }) { return ( <Dynamic component={`h${props.level}`}> {props.children} </Dynamic> );}<Portal> — рендеринг вне дерева
Заголовок раздела «<Portal> — рендеринг вне дерева»import { Portal } from 'solid-js/web';
function Modal(props: { show: boolean; children: any }) { return ( <Show when={props.show}> <Portal mount={document.body}> <div class="modal-overlay"> <div class="modal-content"> {props.children} </div> </div> </Portal> </Show> );}По умолчанию портал рендерится в document.body. Можно задать mount — любой DOM-элемент.
Вложенные контексты и ключи
Заголовок раздела «Вложенные контексты и ключи»// Keyed Show — пересоздаёт дочерние при смене ключа<Show when={user()} keyed> {user => <UserProfile user={user} />}</Show>
// Non-keyed (по умолчанию) — обновляет существующий компонент<Show when={user()}> <UserProfile user={user()!} /></Show>