Диспетчеризация событий

SkateJS — библиотека для создания веб-компонентов на JavaScript с современными подходами к декларативному рендерингу и управлению состоянием. Одной из ключевых возможностей является управление событиями, их подписка, обработка и распространение внутри компонента и за его пределами.


Основы работы с событиями

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

Простейший способ привязки обработчика к событию — использование метода connectedCallback:

import { define, Component, h } from 'skatejs';

class MyButton extends Component {
  connectedCallback() {
    super.connectedCallback();
    this.addEventListener('click', this.handleClick);
  }

  disconnectedCallback() {
    this.removeEventListener('click', this.handleClick);
    super.disconnectedCallback();
  }

  handleClick(event) {
    console.log('Button clicked', event);
  }

  render() {
    return <button>Нажми меня</button>;
  }
}

define('my-button', MyButton);

Здесь важно помнить: подписка на события должна сопровождаться удалением обработчиков в disconnectedCallback, чтобы избежать утечек памяти при удалении компонента из DOM.


Кастомные события

SkateJS позволяет создавать и распространять собственные события с помощью класса CustomEvent. Это особенно полезно для коммуникации между компонентами:

this.dispatchEvent(
  new CustomEvent('item-selected', {
    detail: { id: 42 },
    bubbles: true,
    composed: true
  })
);

Параметры события:

  • detail — объект с данными события.
  • bubbles — позволяет событию всплывать через DOM.
  • composed — позволяет событию пересекать границу Shadow DOM.

Подписка на кастомное событие происходит стандартным методом addEventListener на родительском элементе или глобально.


Обработка событий через свойства

В SkateJS можно задать обработчики событий через свойства компонента. Например, если компонент имеет свойство onClick, оно может быть автоматически связано с событием:

class MyCheckbox extends Component {
  static props = {
    checked: Boolean,
    onToggle: Function
  };

  toggle() {
    this.checked = !this.checked;
    if (this.onToggle) {
      this.onToggle(this.checked);
    }
  }

  render() {
    return <input type="checkbox" checked={this.checked} onCl ick={() => this.toggle()} />;
  }
}

Такой подход разгружает родительский код, позволяя управлять поведением компонента через передачу функций в свойства.


Делегирование событий

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

class ItemList extends Component {
  connectedCallback() {
    super.connectedCallback();
    this.addEventListener('click', this.handleItemClick);
  }

  handleItemClick(event) {
    const item = event.target.closest('li');
    if (item) {
      this.dispatchEvent(new CustomEvent('item-selected', {
        detail: { id: item.dataset.id },
        bubbles: true,
        composed: true
      }));
    }
  }

  render() {
    return (
      <ul>
        <li data-id="1">Элемент 1</li>
        <li data-id="2">Элемент 2</li>
        <li data-id="3">Элемент 3</li>
      </ul>
    );
  }
}

Делегирование позволяет обрабатывать динамически добавленные элементы без дополнительной подписки.


События жизненного цикла

SkateJS предоставляет встроенные события жизненного цикла компонентов, которые можно использовать для управления состоянием и подписками:

  • connectedCallback — срабатывает при подключении к DOM.
  • disconnectedCallback — при удалении из DOM.
  • updated — вызывается после изменения свойств компонента.
  • rendered — после завершения рендера.

Пример использования updated для реакции на изменения свойства:

class MyCounter extends Component {
  static props = { count: Number };

  updated(prevProps) {
    if (prevProps.count !== this.count) {
      console.log(`Счетчик изменен: ${this.count}`);
    }
  }

  render() {
    return <span>{this.count}</span>;
  }
}

Рекомендации по организации событий

  • Удалять подписки при удалении компонента из DOM.
  • Использовать bubbles и composed для событий, которые должны достигать родительских компонентов.
  • Делегировать события для элементов списка или таблицы.
  • Разделять обработку DOM-событий и кастомных событий, чтобы избежать путаницы.
  • Для сложных компонентов стоит создавать собственные методы-диспетчеры событий, обеспечивающие централизованное управление.

Взаимодействие компонентов через события

Компоненты могут общаться, отправляя кастомные события, а родительские компоненты или глобальные слушатели могут их обрабатывать:

class ParentComponent extends Component {
  handleSelection = (event) => {
    console.log('Выбран элемент с ID', event.detail.id);
  };

  render() {
    return <item-list onItem-selected={this.handleSelection}></item-list>;
  }
}

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


Интеграция с внешними событиями

SkateJS-компоненты могут подписываться на глобальные события, такие как window или document:

connectedCallback() {
  super.connectedCallback();
  window.addEventListener('resize', this.handleResize);
}

disconnectedCallback() {
  window.removeEventListener('resize', this.handleResize);
  super.disconnectedCallback();
}

handleResize = (event) => {
  console.log('Размер окна изменился', event.target.innerWidth);
};

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


Принципы диспетчеризации

  1. Локализация событий — события обрабатываются как можно ближе к источнику.
  2. Использование кастомных событий для коммуникации между компонентами.
  3. Минимизация прямых подписок на DOM — предпочтение делегированию и свойствам.
  4. Очистка обработчиков для предотвращения утечек памяти.
  5. Согласованность имен событий — следует придерживаться единого стиля event-name для читаемости кода.

Диспетчеризация в SkateJS сочетает привычные методы DOM и возможности компонентов, обеспечивая мощный и гибкий механизм управления событиями.