Haunted — это современная библиотека для построения веб-компонентов с использованием реактивного состояния и хуков, вдохновленных React. Один из ключевых инструментов управления состоянием в Haunted — это редьюсер, позволяющий централизованно обновлять состояние компонента на основе действий (actions).
Редьюсер в Haunted реализуется через хук useReducer,
который имеет следующую сигнатуру:
const [state, dispatch] = useReducer(reducer, initialState);
Параметры:
reducer — функция, которая принимает текущее состояние
и объект действия, возвращает новое состояние.initialState — начальное состояние компонента.state — текущее состояние, доступное внутри
компонента.dispatch — функция, с помощью которой отправляются
действия для изменения состояния.Функция редьюсера имеет строго определённую форму:
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
}
Ключевые моменты:
...state), а не через
модификацию существующего объекта.action.type) — строковый
идентификатор действия. Любое изменение состояния должно быть выражено
через тип действия.action может содержать дополнительные свойства
(payload), используемые для более сложных обновлений.Пример с payload:
function reducer(state, action) {
switch (action.type) {
case 'SET_NAME':
return { ...state, name: action.payload };
default:
return state;
}
}
Компонент Haunted с редьюсером создаётся следующим образом:
import { html, component, useReducer } from 'haunted';
function Counter() {
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, initialState);
return html`
<div>
<p>Счётчик: ${state.count}</p>
<button @click=${() => dispatch({ type: 'INCREMENT' })}>+</button>
<button @click=${() => dispatch({ type: 'DECREMENT' })}>-</button>
</div>
`;
}
customElements.define('counter-component', component(Counter));
Особенности реализации:
dispatch можно передавать напрямую в обработчики
событий.reducer, что упрощает тестирование и прогнозирование
поведения.Редьюсер удобно использовать для множественных свойств
состояния или сложной логики обновления, когда
простые хуки useState становятся неудобными.
Пример сложного состояния с вложенными объектами:
const initialState = {
user: { name: '', age: 0 },
settings: { darkMode: false }
};
function reducer(state, action) {
switch (action.type) {
case 'SET_NAME':
return { ...state, user: { ...state.user, name: action.payload } };
case 'TOGGLE_DARK_MODE':
return { ...state, settings: { ...state.settings, darkMode: !state.settings.darkMode } };
default:
return state;
}
}
Рекомендации при работе с вложенными объектами:
...) для создания новых копий.Для больших компонентов часто имеет смысл разделять состояние на несколько редьюсеров и использовать их параллельно:
const [userState, userDispatch] = useReducer(userReducer, initialUserState);
const [settingsState, settingsDispatch] = useReducer(settingsReducer, initialSettingsState);
Такой подход повышает читаемость и позволяет логически разделять обработку действий.
Хотя редьюсер сам по себе чистая функция и не должен
содержать побочных эффектов, Haunted позволяет комбинировать редьюсер с
useEffect для асинхронных операций:
useEffect(() => {
if (state.count === 5) {
console.log('Достигнут предел 5');
}
}, [state.count]);
Принцип: редьюсер изменяет только состояние, а все
побочные эффекты выполняются в useEffect, что делает
архитектуру компонентов более предсказуемой.
reducer.action) описывают изменения состояния.useEffect.Эти принципы делают работу с Haunted гибкой, безопасной и масштабируемой, особенно в больших проектах с динамическим состоянием.