Локальное состояние компонента

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

Свойство @state

В Lit для объявления локального состояния используется декоратор @state. Декоратор применяется к свойствам класса компонента и автоматически отслеживает их изменения, вызывая повторный рендер при обновлении значения.

import { LitElement, html } from 'lit';
import { state } from 'lit/decorators.js';

class CounterComponent extends LitElement {
  @state()
  private _count = 0;

  render() {
    return html`
      <div>Счётчик: ${this._count}</div>
      <button @click=${this._increment}>Увеличить</button>
    `;
  }

  private _increment() {
    this._count++;
  }
}

customElements.define('counter-component', CounterComponent);

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

  • Значение свойства не отражается в DOM напрямую и не становится атрибутом HTML-элемента.
  • Изменения автоматически инициируют рендер, что упрощает синхронизацию UI с состоянием.
  • Свойства с @state обычно делают приватными по соглашению, используя префикс _.

Обновление состояния

Изменение состояния должно происходить через присваивание значения свойству с декоратором @state. Lit автоматически определяет, что произошло обновление, и вызывает метод requestUpdate() для повторного рендера. Если необходимо выполнять дополнительные действия при обновлении, можно переопределить метод updated(changedProperties):

updated(changedProperties) {
  if (changedProperties.has('_count')) {
    console.log(`Счётчик изменился с ${changedProperties.get('_count')} на ${this._count}`);
  }
}

changedProperties содержит Map, где ключ — имя свойства, а значение — его предыдущее состояние. Это удобно для реактивных вычислений и синхронизации с внешними данными.

Различие между @property и @state

  • @property используется для публичных свойств компонента, которые могут связываться с атрибутами HTML и поддерживают двунаправленное связывание с внешним миром.
  • @state предназначен исключительно для внутреннего состояния и не отражается в атрибутах.

Использование @state улучшает инкапсуляцию, предотвращая случайные внешние вмешательства в внутреннюю логику компонента.

Комплексные структуры в локальном состоянии

Свойства с декоратором @state могут хранить любые типы данных, включая объекты и массивы. При работе с ними важно создавать новые экземпляры при изменении, чтобы Lit корректно отслеживал обновления.

@state()
private _items = [];

_addItem(item) {
  this._items = [...this._items, item];
}

Прямое изменение массива (this._items.push(item)) не вызовет рендер, потому что Lit отслеживает изменение ссылки на объект, а не его содержимое.

Локальное состояние и рендеринг

Использование локального состояния тесно связано с шаблонами Lit. Любое свойство с @state, используемое внутри метода render(), автоматически отслеживается. Это позволяет строить динамические интерфейсы без ручного управления DOM.

render() {
  return html`
    <ul>
      ${this._items.map(item => html`<li>${item}</li>`)}
    </ul>
    <button @click=${() => this._addItem('Новый элемент')}>Добавить</button>
  `;
}

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

Взаимодействие локального состояния и событий

Локальное состояние удобно использовать в сочетании с пользовательскими событиями. Компонент может изменять внутреннее состояние и уведомлять внешние элементы через событие CustomEvent:

private _increment() {
  this._count++;
  this.dispatchEvent(new CustomEvent('count-changed', {
    detail: { count: this._count },
    bubbles: true,
    composed: true
  }));
}

Это позволяет внутреннему состоянию оставаться инкапсулированным, но при необходимости взаимодействовать с другими компонентами.

Практические рекомендации

  • Декоратор @state использовать для приватных данных, не предназначенных для связывания с HTML-атрибутами.
  • Всегда создавать новые объекты или массивы при изменении состояния, чтобы Lit корректно обнаруживал обновления.
  • Локальное состояние должно быть минимальным — хранить только то, что реально влияет на рендер.

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