Списки и итерации

Stencil предоставляет разработчикам мощные инструменты для создания компонентов с использованием современных возможностей веб-технологий, включая поддержку списков и итераций. В этой главе рассматриваются основные подходы к работе с данными в виде списков, а также принципы и методы их итерации в компонентах Stencil.

Использование циклов для отображения списка элементов

В Stencil для работы с динамическими списками часто используется конструкция циклов. Такой подход позволяет эффективно отображать коллекции данных в HTML-разметке. Основной механизм для этого — это встроенная директива @Watch, которая отслеживает изменения данных и автоматически перерисовывает компонент.

Пример:

import { Component, h, State } from '@stencil/core';

@Component({
  tag: 'item-list',
  styleUrl: 'item-list.css',
  shadow: true
})
export class ItemList {
  @State() items: string[] = ['apple', 'banana', 'cherry'];

  render() {
    return (
      <ul>
        {this.items.map(item => (
          <li>{item}</li>
        ))}
      </ul>
    );
  }
}

В данном примере используется метод .map(), который перебирает массив this.items и генерирует список <li> для каждого элемента. Такой способ позволяет с минимальными усилиями создать динамическое отображение данных.

Реактивность и изменение данных

Stencil использует реактивную модель данных, благодаря которой обновления списка автоматически отражаются в интерфейсе пользователя. Когда данные изменяются, Stencil следит за этим изменением и перерисовывает компонент без необходимости вручную управлять обновлениями DOM.

Для динамического изменения данных в списке можно использовать методы добавления и удаления элементов из состояния:

import { Component, h, State } from '@stencil/core';

@Component({
  tag: 'dynamic-list',
  styleUrl: 'dynamic-list.css',
  shadow: true
})
export class DynamicList {
  @State() items: string[] = [];

  addItem(item: string) {
    this.items = [...this.items, item];
  }

  removeItem(index: number) {
    this.items = this.items.filter((_, i) => i !== index);
  }

  render() {
    return (
      <div>
        <ul>
          {this.items.map((item, index) => (
            <li>
              {item}
              <button onCl ick={() => this.removeItem(index)}>Удалить</button>
            </li>
          ))}
        </ul>
        <button onCl ick={() => this.addItem('новый элемент')}>Добавить элемент</button>
      </div>
    );
  }
}

В этом примере добавление и удаление элементов из списка происходит с использованием реактивных методов addItem и removeItem, что автоматически обновляет DOM, поскольку Stencil отслеживает изменения в @State().

Ключи в итерациях

Когда работаешь с итерациями, особенно в случаях, когда элементы могут изменяться, важно использовать уникальные ключи для каждого элемента в списке. Это помогает Stencil эффективно обновлять только те элементы, которые были изменены, без перерасчета всего списка.

Stencil позволяет использовать атрибут key, чтобы присвоить уникальные идентификаторы каждому элементу списка:

render() {
  return (
    <ul>
      {this.items.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}

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

Обработка асинхронных данных

Иногда данные в списке поступают асинхронно — например, из API. В таких случаях стоит учесть, что Stencil позволяет интегрировать асинхронные запросы с компонентами через использование @State или @Prop.

Пример загрузки данных из API и отображения их в списке:

import { Component, h, State } from '@stencil/core';

@Component({
  tag: 'async-list',
  styleUrl: 'async-list.css',
  shadow: true
})
export class AsyncList {
  @State() items: string[] = [];

  async componentWillLoad() {
    const response = await fetch('https://api.example.com/items');
    const data = await response.json();
    this.items = data.items;
  }

  render() {
    return (
      <ul>
        {this.items.map(item => (
          <li>{item}</li>
        ))}
      </ul>
    );
  }
}

В этом примере при загрузке компонента выполняется асинхронный запрос к API, данные из которого затем сохраняются в состоянии компонента. После получения данных происходит их отрисовка в списке.

Итерация по объектам и массивам

Stencil также поддерживает итерации не только по массивам, но и по объектам. Для работы с объектами можно использовать методы, которые позволяют преобразовывать объект в массив, например, Object.entries().

Пример:

render() {
  const data = {
    apple: 1,
    banana: 2,
    cherry: 3,
  };

  return (
    <ul>
      {Object.entries(data).map(([key, value]) => (
        <li>{key}: {value}</li>
      ))}
    </ul>
  );
}

Здесь Object.entries(data) преобразует объект в массив пар ключ-значение, и в цикле отображаются каждый ключ и его значение.

Итерации с фильтрацией и сортировкой

Списки в Stencil можно не только отображать, но и модифицировать до их отображения. Для этого удобно использовать фильтрацию, сортировку и другие операции с массивами.

Пример сортировки и фильтрации списка:

import { Component, h, State } from '@stencil/core';

@Component({
  tag: 'filtered-list',
  styleUrl: 'filtered-list.css',
  shadow: true
})
export class FilteredList {
  @State() items: string[] = ['apple', 'banana', 'cherry', 'orange'];

  render() {
    const filteredItems = this.items.filter(item => item.startsWith('b')).sort();

    return (
      <ul>
        {filteredItems.map(item => (
          <li>{item}</li>
        ))}
      </ul>
    );
  }
}

В данном примере список сначала фильтруется по элементам, начинающимся на букву “b”, а затем сортируется в алфавитном порядке перед тем, как отобразиться.

Итерации с вложенными компонентами

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

Пример вложенного компонента:

import { Component, h, Prop } from '@stencil/core';

@Component({
  tag: 'item-detail',
  styleUrl: 'item-detail.css',
  shadow: true
})
export class ItemDetail {
  @Prop() item: string;

  render() {
    return <div>Подробности: {this.item}</div>;
  }
}

@Component({
  tag: 'item-list',
  styleUrl: 'item-list.css',
  shadow: true
})
export class ItemList {
  @State() items: string[] = ['apple', 'banana', 'cherry'];

  render() {
    return (
      <ul>
        {this.items.map(item => (
          <li>
            {item} 
            <item-detail item={item}></item-detail>
          </li>
        ))}
      </ul>
    );
  }
}

В этом примере внутри каждого элемента списка рендерится компонент item-detail, который отображает дополнительную информацию о каждом элементе. Это показывает, как можно строить сложные компоненты с вложенной логикой и визуальными элементами.

Заключение

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