Перейти к содержимому

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>
);
}
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.

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-узел не пересоздаётся, даже если массив перетасован.

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>
);
}
ForIndex
СтабильностьСтабильный DOM по объектуСтабильный DOM по индексу
item типЗначение (T)Accessor (T) — сигнал
index типAccessor (number) — сигналЧисло
Лучше дляОбъектов с idПримитивов, матриц
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>
);
}
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>
);
}
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>