Списки и циклы

Haunted — это библиотека для создания веб-компонентов на основе веб-стандартов, использующая функциональный подход, схожий с React Hooks. Основной единицей является компонент, который создается функцией и возвращает шаблон на lit-html. Работа со списками и циклами в Haunted требует понимания реактивного обновления состояния и правильного использования map и repeat.


Работа с массивами и метод map

Метод map является ключевым инструментом для рендеринга списков в Haunted. Каждый элемент массива преобразуется в шаблон lit-html:

import { html, component, useState } from 'haunted';

function TodoList({ todos }) {
  return html`
    <ul>
      ${todos.map(todo => html`<li>${todo}</li>`)}
    </ul>
  `;
}

customElements.define('todo-list', component(TodoList));

Особенности использования map в Haunted:

  • Реактивность: При изменении массива необходимо обновлять состояние компонента через useState, чтобы инициировать повторный рендер.
  • Ключи элементов: Хотя Haunted сам по себе не требует ключей как React, для оптимизации рендеринга больших списков рекомендуется использовать уникальные идентификаторы при необходимости манипуляции отдельными элементами.

Управление состоянием списка

Для динамических списков важно правильно управлять состоянием с помощью useState или пользовательских хуков:

function DynamicList() {
  const [items, setItems] = useState(['Элемент 1', 'Элемент 2']);

  const addItem = () => setItems([...items, `Элемент ${items.length + 1}`]);

  return html`
    <ul>
      ${items.map(item => html`<li>${item}</li>`)}
    </ul>
    <button @click=${addItem}>Добавить элемент</button>
  `;
}

customElements.define('dynamic-list', component(DynamicList));

Ключевые моменты:

  • Состояние items содержит массив для рендеринга.
  • Функция addItem создает новый массив, чтобы сохранить иммутабельность.
  • Каждое изменение состояния автоматически вызывает обновление компонента.

Использование цикла for внутри шаблонов

В Haunted нельзя напрямую использовать стандартный for для генерации шаблонов, так как lit-html ожидает массив шаблонов. Но можно создавать массив заранее:

function NumberList({ count }) {
  const numbers = [];
  for (let i = 1; i <= count; i++) {
    numbers.push(html`<li>${i}</li>`);
  }

  return html`<ul>${numbers}</ul>`;
}

customElements.define('number-list', component(NumberList));

Особенности такого подхода:

  • Позволяет использовать более сложные вычисления для каждого элемента перед рендерингом.
  • Хорошо подходит для списков с большим количеством элементов, когда требуется вычисление до генерации шаблона.

Работа с массивами объектов

Для массивов объектов часто требуется рендерить элементы с множеством свойств:

function UsersList({ users }) {
  return html`
    <ul>
      ${users.map(user => html`
        <li>
          <strong>${user.name}</strong> — ${user.email}
        </li>
      `)}
    </ul>
  `;
}

customElements.define('users-list', component(UsersList));

Особенности:

  • Каждый объект можно деструктуризировать внутри map для упрощения кода.
  • Можно использовать вложенные компоненты для рендеринга сложных структур, сохраняя читаемость.

Оптимизация больших списков с repeat

Для больших массивов рекомендуется использовать директиву repeat из lit-html/directives/repeat.js, которая снижает количество повторных рендеров:

import { repeat } from 'lit-html/directives/repeat.js';

function LargeList({ items }) {
  return html`
    <ul>
      ${repeat(items, item => item.id, item => html`<li>${item.name}</li>`)}
    </ul>
  `;
}

customElements.define('large-list', component(LargeList));

Пояснения:

  • Первый аргумент — массив элементов.
  • Второй аргумент — функция генерации уникального ключа для элемента.
  • Третий аргумент — функция генерации шаблона для элемента.
  • Такой подход минимизирует перерисовку элементов, которые не изменились.

Динамическое обновление списков

Haunted позволяет создавать интерактивные списки, где элементы добавляются или удаляются:

function EditableList() {
  const [items, setItems] = useState(['A', 'B', 'C']);

  const removeItem = index => setItems(items.filter((_, i) => i !== index));

  return html`
    <ul>
      ${items.map((item, index) => html`
        <li>
          ${item} <button @click=${() => removeItem(index)}>Удалить</button>
        </li>
      `)}
    </ul>
  `;
}

customElements.define('editable-list', component(EditableList));

Важно:

  • Любое изменение массива должно создавать новый массив, а не мутировать существующий.
  • Использование стрелочных функций внутри шаблона обеспечивает корректное связывание событий с конкретным элементом.

Комбинация циклов и условий

Списки часто требуют условного рендеринга элементов:

function FilteredList({ items, showEven }) {
  return html`
    <ul>
      ${items
        .filter(item => !showEven || item % 2 === 0)
        .map(item => html`<li>${item}</li>`)}
    </ul>
  `;
}

customElements.define('filtered-list', component(FilteredList));

Особенности:

  • Фильтрация и сортировка выполняются перед map.
  • Можно комбинировать несколько условий, создавая гибкую логику отображения.

С помощью перечисленных подходов Haunted позволяет эффективно работать с массивами, динамическими списками и условиями, обеспечивая реактивность и минимальное количество лишних перерисовок. Комбинация map, repeat и хуков состояния формирует основу управления любыми коллекциями данных в функциональных компонентах.