Haunted — это библиотека для создания веб-компонентов на базе
функций, вдохновлённых React Hooks. Одной из её сильных сторон является
встроенный механизм управления состоянием с помощью
useState, useReducer и useEffect.
Однако для крупных приложений с комплексной логикой часто недостаточно
локального состояния компонентов. В таких случаях применяются внешние
менеджеры состояния.
Внешний менеджер состояния — это централизованное хранилище данных приложения, доступное нескольким компонентам одновременно. Оно отделяет логику управления состоянием от визуальной части, что повышает масштабируемость и тестируемость кода.
Ключевые задачи внешнего менеджера:
В Haunted можно использовать любой внешний менеджер, совместимый с JavaScript, включая Redux, Zustand, MobX, RxJS и другие. Главное — обеспечить реактивную подписку компонентов на изменения состояния.
Redux предоставляет единый источник правды и строгий подход к мутациям состояния через чистые функции-редьюсеры.
Создание хранилища Redux:
import { createStore } from 'redux';
const initialState = { count: 0 };
function reducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
}
export const store = createStore(reducer);
Подключение к Haunted-компоненту:
import { html, component, useState, useEffect } from 'haunted';
import { store } from './store.js';
function Counter() {
const [, setTick] = useState(0);
useEffect(() => {
const unsubscribe = store.subscribe(() => setTick(tick => tick + 1));
return unsubscribe;
}, []);
const state = store.getState();
return html`
<div>
<p>Count: ${state.count}</p>
<button @click=${() => store.dispatch({ type: 'INCREMENT' })}>+</button>
<button @click=${() => store.dispatch({ type: 'DECREMENT' })}>-</button>
</div>
`;
}
customElements.define('counter-component', component(Counter));
Особенности подхода:
useState только для форсирования
ререндеринга.store позволяет автоматически обновлять
компонент при изменении состояния.Zustand — это минималистичный и реактивный менеджер состояния с простым API. Он хорошо сочетается с Haunted благодаря прямой поддержке подписок.
Создание состояния:
import create from 'zustand';
export const useStore = create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
}));
Использование в Haunted-компоненте:
import { html, component } from 'haunted';
import { useStore } from './store.js';
function Counter() {
const count = useStore(state => state.count);
const increment = useStore(state => state.increment);
const decrement = useStore(state => state.decrement);
return html`
<div>
<p>Count: ${count}</p>
<button @click=${increment}>+</button>
<button @click=${decrement}>-</button>
</div>
`;
}
customElements.define('counter-component', component(Counter));
Преимущества подхода:
В Haunted можно использовать useEffect для интеграции с
любым менеджером состояния, поддерживающим подписки. Ключевая задача —
вызвать обновление компонента при изменении состояния.
Пример:
useEffect(() => {
const unsubscribe = externalStore.subscribe(() => {
setTick(tick => tick + 1);
});
return unsubscribe;
}, []);
В этом случае компонент будет реагировать на изменения глобального состояния, не храня его локально.
Многие внешние менеджеры состояния предоставляют встроенные механизмы для асинхронных операций:
redux-thunk,
redux-saga) для обработки промисов и эффектов.subscribe.Пример асинхронного увеличения счетчика через Zustand:
incrementAsync: async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
set(state => ({ count: state.count + 1 }));
}
useState только для состояния, относящегося к конкретному
компоненту.useEffect
должен корректно отписываться при размонтировании компонентов.Haunted в сочетании с внешними менеджерами состояния позволяет создавать масштабируемые, реактивные веб-компоненты, сохраняя простоту функционального подхода и гибкость архитектуры. Такой подход обеспечивает строгую централизацию данных, синхронизацию UI и легкую интеграцию с асинхронной логикой.