Higher-Order Components (HOC) в SkateJS представляют собой паттерн для повторного использования логики компонентов, позволяющий создавать новые компоненты на основе существующих. В отличие от обычных компонентов, HOC не изменяют исходный компонент напрямую, а возвращают расширенную версию с дополнительными возможностями.
HOC в SkateJS реализуются как функции, принимающие компонент и возвращающие новый компонент. Важно понимать, что HOC не должны рендерить JSX напрямую; их задача — инъекция логики, свойств или стилей в базовый компонент.
Пример базового синтаксиса HOC:
import { define, Component, h } from 'skatejs';
function withLogging(BaseComponent) {
return class extends BaseComponent {
connectedCallback() {
super.connectedCallback && super.connectedCallback();
console.log(`${BaseComponent.is} подключён к DOM`);
}
};
}
class MyButton extends Component {
static get is() { return 'my-button'; }
renderCallback() {
return <button>Нажми меня</button>;
}
}
define(MyButton);
const LoggedButton = withLogging(MyButton);
define(LoggedButton);
В этом примере withLogging добавляет функциональность
логирования, не вмешиваясь в исходный компонент
MyButton.
HOC можно использовать для добавления или изменения свойств
компонента. SkateJS предоставляет API для декларативного
описания свойств через static get props().
Пример расширения компонента с новым свойством:
function withCounter(BaseComponent) {
return class extends BaseComponent {
static get props() {
return {
...BaseComponent.props,
count: { attribute: true, default: 0 }
};
}
increment() {
this.count++;
}
renderCallback() {
return (
<div>
{super.renderCallback()}
<span>Счётчик: {this.count}</span>
<button oncl ick={() => this.increment()}>+</button>
</div>
);
}
};
}
class SimpleBox extends Component {
static get is() { return 'simple-box'; }
renderCallback() {
return <div>Контент блока</div>;
}
}
define(SimpleBox);
const CounterBox = withCounter(SimpleBox);
define(CounterBox);
Здесь HOC withCounter добавляет свойство
count и методы для его изменения, при этом исходный
компонент остаётся неизменным.
SkateJS позволяет цепочку HOC, создавая компонент с множественными расширениями. Порядок применения имеет значение: первый HOC оборачивает компонент, второй — уже результат работы первого.
const EnhancedButton = withLogging(withCounter(MyButton));
define(EnhancedButton);
В этом случае сначала добавляется логирование, затем счётчик. Такой подход позволяет модульно строить сложное поведение компонентов без дублирования кода.
HOC могут взаимодействовать с методами жизненного цикла SkateJS:
connectedCallback()disconnectedCallback()updated()renderCallback()При использовании HOC необходимо всегда вызывать
super, чтобы сохранить оригинальное поведение
базового компонента.
function withTimer(BaseComponent) {
return class extends BaseComponent {
connectedCallback() {
super.connectedCallback && super.connectedCallback();
this._timer = setInterval(() => console.log('Тик'), 1000);
}
disconnectedCallback() {
clearInterval(this._timer);
super.disconnectedCallback && super.disconnectedCallback();
}
};
}
Это гарантирует корректное взаимодействие с жизненным циклом и предотвращает утечки ресурсов.
SkateJS поддерживает Shadow DOM по умолчанию. HOC может добавлять содержимое в теневой корень компонента, расширяя его визуальную структуру:
function withWrapper(BaseComponent) {
return class extends BaseComponent {
renderCallback() {
return (
<div class="wrapper">
{super.renderCallback()}
</div>
);
}
};
}
Такой подход позволяет оборачивать содержимое компонента в дополнительные контейнеры или стилизованные блоки без изменения исходного компонента.
renderCallback сложной логикой, лучше добавлять
свойства и обработчики.super. Любое расширение
методов жизненного цикла должно сохранять поведение базового
компонента.HOC в SkateJS идеально подходят для следующих задач:
Эта архитектура позволяет строить гибкие, модульные и повторно используемые компоненты, соответствующие принципам современного веб-разработки на базе SkateJS.