Skip to Content
👋 Добро пожаловать в документацию lite-fsm!
Пакеты@lite-fsm/middleware

@lite-fsm/middleware

@lite-fsm/middleware расширяет MachineManager: подключает Immer, отправляет события в Redux DevTools и даёт место для собственных middleware, например логирования, аналитики или сохранения состояния.

Установка

npm install @lite-fsm/middleware immer

Что такое middleware

Middleware в lite-fsm следует тому же паттерну, что и в Redux. Это функция формы:

const middleware = (api) => (next) => (action) => { // код до next — видит состояние до перехода const result = next(action); // код после next — видит состояние после перехода return result; };
MiddlewareApiЧто даёт
getState()Текущий state менеджера.
transition(action)Отправить action через всю middleware-цепочку.
replaceReducer(enhancer)Обернуть root reducer.
onTransition(cb)Подписка на каждый успешный dispatch.
condition(predicate)Promise, резолвится на matching action.

Правила:

  • порядок pre-next = порядок регистрации, post-next — обратный (как в Redux);
  • код до next(action) видит состояние до перехода, код после — состояние после перехода и уже отработавших подписчиков;
  • effects запускаются после возврата всей middleware-цепочки;
  • чтобы заблокировать action — не вызывать next(action);
  • чтобы изменить — вызвать next(modifiedAction);
  • transition возвращает action, дошедший до reducer-а.

Импорт

Импортируйте middleware из общего пакета или из точечных модулей:

// Общая точка входа — оба middleware import { immerMiddleware, devToolsMiddleware } from "@lite-fsm/middleware"; // Точечные импорты помогают не включать лишний код import { immerMiddleware } from "@lite-fsm/middleware/immer"; import { devToolsMiddleware } from "@lite-fsm/middleware/devTools";

Встроенные middleware

immerMiddleware

Оборачивает root reducer в produce(...) из библиотеки Immer . Это позволяет мутировать state напрямую и не возвращать ничего из reducer-а.

import { MachineManager, createMachine } from "@lite-fsm/core"; import { immerMiddleware } from "@lite-fsm/middleware/immer"; const todos = createMachine({ config: { READY: { ADD_TODO: null, TOGGLE_TODO: null, REMOVE_TODO: null, }, }, initialState: "READY", initialContext: { items: [] as { id: string; text: string; completed: boolean }[], }, reducer: (state, action) => { switch (action.type) { case "ADD_TODO": state.context.items.push({ id: action.payload.id, text: action.payload.text, completed: false, }); return; case "TOGGLE_TODO": { const todo = state.context.items.find((t) => t.id === action.payload.id); if (todo) todo.completed = !todo.completed; return; } case "REMOVE_TODO": state.context.items = state.context.items.filter((t) => t.id !== action.payload.id); return; } }, }); const manager = MachineManager({ todos }, { middleware: [immerMiddleware] });

immerMiddleware несёт маркер __liteFsmAllowVoidReducer, поэтому core разрешает reducer вернуть void. Если reducer всё-таки вернул объект, поля верхнего уровня копируются в draft, а неизменённые вложенные объекты сохраняют ссылки.

devToolsMiddleware

Подключает Redux DevTools Extension .

import { MachineManager } from "@lite-fsm/core"; import { devToolsMiddleware } from "@lite-fsm/middleware/devTools"; const manager = MachineManager( { counter }, { middleware: [devToolsMiddleware({ blacklistActions: ["TIMER_TICK", "AUTO_SAVE"] })], }, );
ОпцияНазначение
blacklistActions?: string[]Action types, которые не отправляются в DevTools.

Без window.__REDUX_DEVTOOLS_EXTENSION__ middleware пропускает события без отправки в DevTools. Если extension доступен: connect → init → send на каждый action; JUMP_TO_ACTION / ROLLBACK восстанавливают state через replaceReducer.

Важно: DevTools middleware находится в alpha-версии. Базовое логирование событий и time-travel restore работают; экспорт/импорт состояний и тестирование на основе записей пока не являются стабильным контрактом.

Создание собственного middleware

Middleware — функция формы (api) => (next) => (action) => action:

import type { Middleware } from "@lite-fsm/core"; const logger: Middleware = (api) => (next) => (action) => { console.group(`Action: ${action.type}`); console.log("before:", api.getState()); const result = next(action); console.log("after:", api.getState()); console.groupEnd(); return result; }; const manager = MachineManager({ counter }, { middleware: [logger] });

Аналитика

const analytics: Middleware = (api) => (next) => (action) => { const startedAt = performance.now(); const result = next(action); const duration = performance.now() - startedAt; window.analytics?.track("State Transition", { action: action.type, duration, timestamp: new Date().toISOString(), }); return result; };

Персистентность в localStorage

const persist = (storageKey = "lite-fsm-state"): Middleware => (api) => (next) => (action) => { const result = next(action); try { localStorage.setItem(storageKey, JSON.stringify(api.getState())); } catch (err) { console.error("Failed to persist state:", err); } return result; }; const manager = MachineManager({ app }, { middleware: [persist("my-app-state")] });

Для переноса состояния между сессиями используйте встроенные manager.dehydrate() / manager.hydrate() — они корректно работают с акторами, версиями схемы и dehydrate-хуками. См. Сохранение состояния.

Централизованная обработка ошибок

const errorHandler = (handler: (err: unknown, action: AnyEvent) => void): Middleware => (api) => (next) => (action) => { try { return next(action); } catch (error) { handler(error, action); return action; } }; const manager = MachineManager( { app }, { middleware: [ errorHandler((error, action) => { reportErrorToService(error, { action }); showErrorNotification(`Произошла ошибка: ${(error as Error).message}`); }), ], }, );

Порядок выполнения middleware

Middleware применяются в порядке регистрации до next, и в обратном — после next:

Action -> M1 -> M2 -> Reducer -> M2 -> M1 -> Result

После того как вся цепочка middleware вернётся, manager синхронно вызывает подписчиков onTransition, а уже после них — эффекты состояний.

Заключение

Middleware в lite-fsm дают единый механизм для интеграций: Immer, DevTools, логирование, аналитика, сохранение состояния и обработка ошибок. При этом они не меняют основную модель FSM: reducer и effects остаются отделены от интеграционного слоя.

Last updated on