State machine (машина состояний) — это модель управления состояниями, которая позволяет описывать поведение компонента или приложения через конечное количество состояний и переходов между ними. В Preact использование state machine упрощает управление сложной логикой компонентов, особенно когда состояние зависит от множества факторов и событий.
Состояние (State) — это текущее положение компонента
в его жизненном цикле. В традиционном React/Preact это объект или
примитив, задаваемый через useState или
useReducer.
Событие (Event) — действие или триггер, который инициирует переход из одного состояния в другое. Событие может быть вызвано пользовательским взаимодействием, таймером или изменением данных.
Переход (Transition) — определяет, как одно состояние изменяется в другое под воздействием события. Переходы часто описываются в виде таблицы или объекта, где ключ — текущее состояние и событие, а значение — новое состояние.
Стартовое состояние (Initial State) — состояние, в
котором компонент находится при инициализации. В Preact оно обычно
задается в хуке useState или useReducer.
Для интеграции машины состояний с Preact можно использовать хуки и
чистые функции. Рассмотрим подход с useReducer, который
идеально подходит для реализации state machine:
import { h } from 'preact';
import { useReducer } from 'preact/hooks';
const initialState = { status: 'idle' };
function reducer(state, action) {
switch (state.status) {
case 'idle':
if (action.type === 'FETCH') return { status: 'loading' };
return state;
case 'loading':
if (action.type === 'SUCCESS') return { status: 'success', data: action.data };
if (action.type === 'ERROR') return { status: 'error', error: action.error };
return state;
case 'success':
case 'error':
if (action.type === 'RESET') return { status: 'idle' };
return state;
default:
return state;
}
}
function FetchComponent() {
const [state, dispatch] = useReducer(reducer, initialState);
const fetchData = async () => {
dispatch({ type: 'FETCH' });
try {
const response = await fetch('/api/data');
const data = await response.json();
dispatch({ type: 'SUCCESS', data });
} catch (error) {
dispatch({ type: 'ERROR', error });
}
};
return (
<div>
{state.status === 'idle' && <button onCl ick={fetchData}>Загрузить данные</button>}
{state.status === 'loading' && <p>Загрузка...</p>}
{state.status === 'success' && <pre>{JSON.stringify(state.data, null, 2)}</pre>}
{state.status === 'error' && <p>Ошибка: {state.error.message}</p>}
</div>
);
}
Ключевые моменты данного подхода:
useReducer позволяет централизованно управлять всеми
состояниями и переходами.Для сложных компонентов удобно описывать переходы в виде объекта:
const transitions = {
idle: { FETCH: 'loading' },
loading: { SUCCESS: 'success', ERROR: 'error' },
success: { RESET: 'idle' },
error: { RESET: 'idle' },
};
function reducer(state, action) {
const nextState = transitions[state.status]?.[action.type];
return nextState ? { ...state, status: nextState, ...action.payload } : state;
}
Такой подход делает логику декларативной, упрощает отладку и документирование.
State machine хорошо интегрируется с хуками жизненного цикла:
useEffect — для запуска побочных
эффектов при переходе в определённое состояние:useEffect(() => {
if (state.status === 'loading') {
const timer = setTimeout(() => dispatch({ type: 'ERROR', error: new Error('Timeout') }), 5000);
return () => clearTimeout(timer);
}
}, [state.status]);
useMemo / useCallback —
для оптимизации вычислений и обработки событий, чтобы не пересоздавать
функции при каждом рендере.В крупных приложениях часто возникает необходимость комбинировать несколько машин состояний. В Preact это реализуется через вложенные редьюсеры или отдельные компоненты, управляющие своей локальной машиной:
function ParentComponent() {
const [parentState, parentDispatch] = useReducer(parentReducer, parentInitialState);
return (
<div>
{parentState.status === 'active' && <ChildComponent />}
<button onCl ick={() => parentDispatch({ type: 'TOGGLE' })}>Переключить</button>
</div>
);
}
Каждый дочерний компонент может иметь собственную машину состояний, при этом события родителя могут инициировать переходы в дочерних машинах через пропсы или контекст.
useReducer, useEffect)
идеально подходит для реализации таких моделей.State machine становится мощным инструментом для управления сложной логикой в Preact-приложениях, превращая хаотичное состояние в структурированную, легко управляемую систему.