Provider паттерн в Stencil используется для управления состоянием
приложения и передачи данных между компонентами без необходимости
прокидывать свойства через каждый уровень иерархии. Этот подход особенно
актуален в больших приложениях, где компоненты могут находиться на
разных уровнях DOM, а прямое использование props или
событий становится неудобным.
В основе паттерна лежит два ключевых элемента: Provider и Consumer. Provider создаёт контекст данных и обеспечивает доступ к этим данным для всех дочерних компонентов, которые подписаны на этот контекст через Consumer.
Stencil предоставляет встроенный механизм контекста через функции
createContext и provideContext. Контекст — это
объект, содержащий состояние и методы для его изменения.
import { createContext } from '@stencil/core';
export interface AppContextState {
user: string;
isAuthenticated: boolean;
login: (user: string) => void;
logout: () => void;
}
export const AppContext = createContext<AppContextState>();
Ключевой момент: интерфейс контекста должен описывать структуру состояния и функции управления этим состоянием. Это позволяет TypeScript обеспечивать строгую типизацию и предотвращать ошибки при использовании данных в компонентах.
Provider компонент создаётся с использованием декоратора
@Component и метода provideContext. Он
инкапсулирует логику управления состоянием и передаёт данные через
контекст:
import { Component, h, State, Provide } from '@stencil/core';
import { AppContext, AppContextState } from './app-context';
@Component({
tag: 'app-provider',
shadow: true,
})
export class AppProvider {
@State() user: string = '';
@State() isAuthenticated: boolean = false;
login = (user: string) => {
this.user = user;
this.isAuthenticated = true;
};
logout = () => {
this.user = '';
this.isAuthenticated = false;
};
@Provide() appContext: AppContextState = {
user: this.user,
isAuthenticated: this.isAuthenticated,
login: this.login,
logout: this.logout,
};
render() {
return <slot />;
}
}
Особенности реализации:
render возвращает <slot />, чтобы
все дочерние элементы могли быть вложены в Provider.Дочерние компоненты получают доступ к данным контекста с помощью
декоратора @Consume:
import { Component, h, Consume } from '@stencil/core';
import { AppContext, AppContextState } from './app-context';
@Component({
tag: 'user-status',
shadow: true,
})
export class UserStatus {
@Consume(AppContext) appContext: AppContextState;
render() {
return (
<div>
{this.appContext.isAuthenticated
? `Пользователь: ${this.appContext.user}`
: 'Гость'}
<button onCl ick={() => this.appContext.logout()}>Выйти</button>
</div>
);
}
}
Ключевые моменты:
@State и props.@State или
@Prop внутри Provider. Простое присваивание
объекта контекста без реактивных полей не вызовет обновление
Consumer.@Event) для
передачи сигналов, которые не влияют на состояние, но требуют
уведомления компонентов.AuthContext, ThemeContext,
CartContext.Provider паттерн является базовым инструментом для построения масштабируемых, реактивных приложений на Stencil, обеспечивая чёткую структуру передачи данных и упрощая поддержку больших проектов.