Композиция событий

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


События и их обработка

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

Простейший пример объявления события в Atomico:

import { c, useEvent } from "atomico";

function MyButton() {
    const handleClick = useEvent("custom-click", { bubbles: true });

    return (
        <button onCl ick={() => handleClick({ message: "Hello" })}>
            Нажми меня
        </button>
    );
}

customElements.define("my-button", c(MyButton));

Здесь useEvent создаёт событие с именем "custom-click" и позволяет передавать объект с данными при его вызове. Параметр bubbles: true делает событие всплывающим, что позволяет его перехватывать на уровне родительских компонентов.


Подписка на события

Для обработки событий в Atomico используется стандартная подписка через JSX или через API useEventTarget. Это позволяет сочетать внутренние события компонента с внешними обработчиками.

function App() {
    const handleCustomClick = (event) => {
        console.log(event.detail.message);
    };

    return (
        <my-button onCustom-click={handleCustomClick}></my-button>
    );
}

Ключевой момент: имя события в JSX onCustom-click соответствует имени, переданному в useEvent с учётом преобразования в camelCase для синтаксиса JSX.


Композиция нескольких событий

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

function ParentComponent() {
    const handleChildEvent = (event) => {
        const newDetail = { combinedMessage: `От ребёнка: ${event.detail.message}` };
        dispatch("parent-event", newDetail);
    };

    const dispatch = useEvent("parent-event");

    return (
        <div>
            <my-button onCustom-click={handleChildEvent}></my-button>
            <div onParent-event={(e) => console.log(e.detail.combinedMessage)}></div>
        </div>
    );
}

Здесь ParentComponent принимает событие от дочернего my-button, модифицирует данные и создаёт собственное событие "parent-event", которое может быть перехвачено другими элементами.


Слияние и фильтрация событий

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

import { useState } from "atomico";

function MultiEventComponent() {
    const [messages, setMessages] = useState([]);

    const handleEvent = (event) => {
        setMessages(prev => [...prev, event.detail.message]);
    };

    return (
        <div>
            <my-button onCustom-click={handleEvent}></my-button>
            <my-button onCustom-click={handleEvent}></my-button>
            <ul>
                {messages.map(msg => <li>{msg}</li>)}
            </ul>
        </div>
    );
}

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


Создание цепочек событий

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

function Step1({ onNext }) {
    const triggerNext = useEvent("step1-complete");

    return <button onCl ick={() => { triggerNext({ step: 1 }); onNext(); }}>Step 1</button>;
}

function Step2() {
    const handleStep1 = (event) => {
        console.log("Step 1 завершён:", event.detail.step);
    };

    return <step1 onStep1-complete={handleStep1}></step1>;
}

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


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

  • Использовать всплывающие события для передачи информации от дочерних компонентов к родительским.
  • Фильтровать события по типу и контексту, чтобы избегать лишней обработки.
  • Комбинировать состояния и события, используя реактивные хуки useState и useEffect, чтобы события сразу обновляли интерфейс.
  • Разделять события на внутренние и внешние: внутренние используются только внутри компонента, внешние — для коммуникации между компонентами.
  • Избегать циклических цепочек событий, которые могут вызвать бесконечный рендер.

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

  1. detail — основной контейнер данных события.
  2. bubbles — контролирует всплытие события.
  3. composed — позволяет событию проходить через Shadow DOM границы.
  4. cancelable — даёт возможность отменять стандартное поведение.
const customEvent = new CustomEvent("example", {
    detail: { value: 42 },
    bubbles: true,
    composed: true,
    cancelable: false
});

В Atomico useEvent создаёт обёртку над этим API, упрощая синтаксис и интеграцию с JSX.


С таким подходом композиция событий становится инструментом построения реактивных и масштабируемых интерфейсов, где данные проходят через цепочки компонентов, объединяются и фильтруются, а состояние UI автоматически синхронизируется с событиями.