Skip to content

Списки

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

Как выводить меняющиеся списки: Each узнаёт строки по ключу и переставляет готовые DOM-узлы вместо перерисовки всего списка.

<Each>: ключ — это личность строки

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

<ul>
  <Each each={todos} by={(t) => t.id}>
    {(t) => <TodoRow todo={t} />}
  </Each>
</ul>;

by играет роль key из React, но с более сильным следствием: на каждый ключ живёт одно построенное поддерево. Пока ключ присутствует в списке, его строка не перерендеривается — вообще. При изменении порядка DOM-узлы переставляются минимальным числом перемещений (LIS-алгоритм), фокус сохраняется.

Из этого следствия:

  • строка строится один раз → render получает элемент на момент появления ключа. Если содержимое строки должно меняться, внутрь нужно передавать State (см. ниже);
  • дубликаты ключей игнорируются (остаётся первый) — ключ обязан быть уникальным;
  • удаление ключа из списка уничтожает поддерево строки каскадно: подписки, таймеры, вложенные регионы — всё убирается деревом владения.

Меняющееся содержимое строки

Если элементы списка обновляются «на месте» (тот же id, другие поля), прокидывайте в строку state её данных, а не снапшот:

tsx
function TodoRow(props: { todo: State<Todo> }) {
  const done = props.todo.map((t) => t.done);
  const text = props.todo.map((t) => t.text);
  return <li class={done.map((d) => (d ? "done" : ""))}>{text}</li>;
}

<Each each={todos} by={(t) => t.id}>
  {(t) => (
    <TodoRow todo={todos.map((xs) => xs.find((x) => x.id === t.id) ?? t)} />
  )}
</Each>;

Строка построена один раз, а её текст и класс — живые привязки. Обновление поля патчит текстовый узел, не трогая структуру.

Фильтрация и сортировка — производные списки

Отфильтрованный список — это map над исходным, а не отдельное состояние:

tsx
const visible = combine(todos, filter, (xs, f) =>
  f === "all" ? xs : xs.filter((t) => t.done === (f === "done")),
);

<Each each={visible} by={(t) => t.id}>{(t) => <TodoRow …/>}</Each>;

Переключение фильтра меняет состав ключей; строки, чьи ключи уцелели, переезжают без перерендера.

Когда <Each> не нужен

Статический список (данные не меняются за время жизни поддерева) — это обычный array.map прямо в JSX:

tsx
<ul>
  {items.map((i) => (
    <li>{i.name}</li>
  ))}
</ul>

<Each> нужен только когда список — State.


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