Передача в дочерние компоненты

Haunted — это библиотека для создания веб-компонентов на базе современного JavaScript с использованием реактивного состояния. Одним из ключевых аспектов работы с компонентами является передача данных от родителя к дочернему компоненту. В Haunted это реализуется через свойства (properties) и события (events).


Свойства компонентов

Свойства в Haunted используются для передачи данных внутрь компонента и реактивного обновления интерфейса при их изменении. Создание свойств осуществляется с помощью функции component и встроенного хука useState или через стандартные getter/setter.

Пример базового компонента с свойствами:

import { component, html } from 'haunted';

function Child({ name, age }) {
  return html`<p>Имя: ${name}, Возраст: ${age}</p>`;
}

customElements.define('my-child', component(Child));

В родительском компоненте свойства передаются напрямую через HTML-атрибуты:

import { component, html } from 'haunted';

function Parent() {
  const childName = 'Алексей';
  const childAge = 25;

  return html`<my-child name="${childName}" age="${childAge}"></my-child>`;
}

customElements.define('my-parent', component(Parent));

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

  • Свойства автоматически преобразуются в строки при передаче через HTML. Для передачи сложных объектов требуется использовать .property или Object.assign.
  • Использование camelCase в JavaScript преобразуется в kebab-case для HTML-атрибутов. Например, userNameuser-name.

Деструктуризация свойств

Haunted поддерживает деструктуризацию объектов свойств напрямую в аргументах функции компонента. Это упрощает доступ к передаваемым данным и делает код чище:

function Profile({ user }) {
  const { name, email } = user;
  return html`<div>${name} — ${email}</div>`;
}

customElements.define('user-profile', component(Profile));

Передача объекта через свойство:

const user = { name: 'Мария', email: 'maria@example.com' };
html`<user-profile .user="${user}"></user-profile>`;

Обратите внимание на точку перед атрибутом .user — это синтаксис для передачи объекта напрямую, а не строки.


Свойства с реактивностью

Для реактивного обновления дочернего компонента при изменении значения в родителе используют useState или useReducer:

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

function Parent() {
  const [count, setCount] = useState(0);

  return html`
    <button @click="${() => setCount(count + 1)}">Увеличить</button>
    <counter-display .value="${count}"></counter-display>
  `;
}

function CounterDisplay({ value }) {
  return html`<p>Счётчик: ${value}</p>`;
}

customElements.define('parent-component', component(Parent));
customElements.define('counter-display', component(CounterDisplay));

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

  • Изменение состояния в родителе автоматически передаётся дочернему компоненту.
  • Использование .value="${count}" гарантирует передачу числа как JavaScript-значения, а не строки.

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

Помимо передачи данных вниз, часто требуется отправлять события вверх, к родителю. Для этого в Haunted используют стандартный механизм CustomEvent.

Пример:

function Child({ onIncrement }) {
  return html`<button @click="${() => onIncrement()}">Добавить</button>`;
}

customElements.define('increment-button', component(Child));

function Parent() {
  const [value, setValue] = useState(0);

  const handleIncrement = () => setValue(value + 1);

  return html`
    <p>Значение: ${value}</p>
    <increment-button .onIncrem ent="${handleIncrement}"></increment-button>
  `;
}

customElements.define('counter-parent', component(Parent));

Важные детали:

  • Для передачи функций используют .property="${function}".
  • События могут быть также реализованы через dispatchEvent внутри дочернего компонента для полной совместимости с DOM:
function Child() {
  const increment = () => {
    const event = new CustomEvent('increment', { bubbles: true, composed: true });
    this.dispatchEvent(event);
  };

  return html`<button @click="${increment}">Добавить</button>`;
}

customElements.define('increment-child', component(Child));

В родителе подписка на событие осуществляется через @increment:

html`<increment-child @increment="${() => setValue(value + 1)}"></increment-child>`;

Динамическая передача дочерних компонентов

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

function Container({ content }) {
  return html`<div class="container">${content()}</div>`;
}

function App() {
  const content = () => html`<p>Динамический текст</p>`;
  return html`<container-component .content="${content}"></container-component>`;
}

customElements.define('container-component', component(Container));
customElements.define('app-root', component(App));

Рекомендации по архитектуре

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

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