API Справочник
В этом разделе приведена документация по всем функциям и типам библиотеки lite-fsm.
Основной модуль
Основной модуль предоставляет функции для создания и управления конечными автоматами.
createMachine
createMachine<C, T, E, P = FSMEvent<E, any>, D = {}>(cfg: MachineConfig<C, T, P, D>): Machine<C, T, P, D>Функция для создания конечного автомата.
Параметры:
cfg: Объект конфигурации автомата.
Возвращает:
Объект автомата.
Пример:
import { createMachine } from "lite-fsm";
const toggleMachine = createMachine({
config: {
INACTIVE: {
TOGGLE: "ACTIVE",
},
ACTIVE: {
TOGGLE: "INACTIVE",
},
},
initialState: "INACTIVE",
initialContext: {
lastToggled: null,
},
});MachineManager
MachineManager<M extends Record<string, Machine<any, any, any, any>>>(
machines: M,
opts?: {
middleware?: Middleware<MachinesState<M>, any>[];
onError?: (err: any) => void;
}
): MachineManager<M>Функция для создания менеджера автоматов.
Параметры:
machines: Объект с автоматами, где ключи - имена автоматов, а значения - объекты автоматов.opts: (Опционально) Объект с дополнительными опциями.middleware: Массив функций middleware для расширения функциональности.onError: Обработчик ошибок, возникающих при выполнении эффектов.
Возвращает:
Менеджер автоматов.
Пример:
import { MachineManager, createMachine } from "lite-fsm";
import { immerMiddleware } from "lite-fsm/middleware";
const toggleMachine = createMachine({
// ...конфигурация
});
const counterMachine = createMachine({
// ...конфигурация
});
// Создание менеджера без дополнительных опций
const simpleManager = MachineManager({
toggle: toggleMachine,
counter: counterMachine,
});
// Создание менеджера с middleware и обработчиком ошибок
const advancedManager = MachineManager(
{
toggle: toggleMachine,
counter: counterMachine,
},
{
middleware: [
immerMiddleware,
// Пользовательский middleware для логирования
(api) => (next) => (action) => {
console.log("Action:", action);
const result = next(action);
console.log("New state:", api.getState());
return result;
},
],
onError: (error) => {
console.error("Error in machine effect:", error);
// Можно отправить ошибку в сервис мониторинга
},
},
);Методы MachineManager
transition
transition<E extends string, P extends Record<string, any>>(
action: { type: E; payload?: P }
): voidВыполняет переход состояния для всех автоматов.
Параметры:
action: Объект действия.type: Тип события.payload: (Опционально) Полезная нагрузка, которая будет доступна в редьюсерах и эффектах.
Пример:
// Отправить событие с payload
manager.transition({
type: "TOGGLE",
payload: { lastToggled: new Date() },
});
// Отправить событие без payload
manager.transition({ type: "RESET" });getState
getState(): { [K in keyof M]: { state: State<C>; context: T } }Возвращает текущее состояние и контекст всех автоматов.
Возвращает:
Объект, в котором ключи - имена автоматов, а значения - объекты с состоянием и контекстом.
Пример:
const state = manager.getState();
console.log(state.toggle.state); // Текущее состояние автомата toggle
console.log(state.counter.context); // Текущий контекст автомата counteronTransition
onTransition(
callback: (
prevState: { [K in keyof M]: { state: State<C>; context: T } },
nextState: { [K in keyof M]: { state: State<C>; context: T } },
action: { type: string; payload?: any }
) => void
): () => voidПодписывает функцию на события перехода состояний.
Параметры:
callback: Функция обратного вызова, которая будет вызываться при каждом переходе состояния.
Возвращает:
Функция для отмены подписки.
Пример:
const unsubscribe = manager.onTransition((prevState, nextState, action) => {
console.log("Предыдущее состояние:", prevState);
console.log("Новое состояние:", nextState);
console.log("Действие:", action);
});
// Позже, когда подписка больше не нужна
unsubscribe();setDependencies
setDependencies<D extends Record<string, any> = {}>(dependencies: D | ((deps: D) => D)): voidУстанавливает зависимости, которые будут доступны в эффектах. Можно передать либо объект с зависимостями, либо функцию обновления, которая получает текущие зависимости и возвращает новые.
Параметры:
dependencies: Объект с зависимостями или функция, принимающая текущие зависимости и возвращающая новые.
Пример объекта:
// Установка зависимостей напрямую через объект
manager.setDependencies({
services: {
api: {
fetchData: async () => {
const response = await fetch("/api/data");
return response.json();
},
},
storage: {
saveItem: (key, value) => localStorage.setItem(key, JSON.stringify(value)),
getItem: (key) => JSON.parse(localStorage.getItem(key) || "null"),
},
},
utils: {
formatDate: (date) => new Intl.DateTimeFormat().format(date),
generateId: () => Math.random().toString(36).substring(2),
},
});Пример функции обновления:
// Обновление существующих зависимостей с сохранением предыдущих значений
manager.setDependencies((prevDeps) => ({
...prevDeps,
services: {
...prevDeps.services,
analytics: {
trackEvent: (eventName, params) => {
console.log(`Analytics event: ${eventName}`, params);
// Отправка события в аналитику
},
},
},
// Добавление новых зависимостей
env: {
isDevelopment: process.env.NODE_ENV === "development",
apiUrl: process.env.API_URL,
},
}));Функциональный подход особенно полезен для поэтапного обновления зависимостей или для добавления новых зависимостей без потери существующих.
replaceReducer
replaceReducer(
callback: (
reducer: (state: MachinesState<M>, action: A) => MachinesState<M>
) => (state: MachinesState<M>, action: A) => MachinesState<M>
): voidЗаменяет редьюсер менеджера автоматов. Этот метод используется middleware для модификации поведения обработки состояний.
Параметры:
callback: Функция, которая принимает текущий редьюсер и возвращает новый редьюсер.
Пример:
// Пример создания middleware с использованием replaceReducer
const loggingMiddleware = (api) => {
// Заменяем редьюсер, добавляя логирование
api.replaceReducer((reducer) => {
return (state, action) => {
console.log("Предыдущее состояние:", state);
console.log("Действие:", action);
const result = reducer(state, action);
console.log("Новое состояние:", result);
return result;
};
});
return (next) => (action) => next(action);
};
// Использование middleware
const manager = MachineManager({ counter: counterMachine }, { middleware: [loggingMiddleware] });condition
condition(
predicate: (action: A) => boolean
): Promise<boolean>Создает промис, который резолвится, когда переход, удовлетворяющий предикату, происходит в системе. Полезно для создания последовательностей асинхронных операций с ожиданием определенных состояний.
Параметры:
predicate: Функция, которая принимает действие и возвращает булево значение.
Возвращает:
Промис, который резолвится со значением true, когда предикат возвращает true для какого-либо действия.
Пример:
// Использование condition в эффекте
const machine = createMachine({
config: {
IDLE: {
START: "PROCESSING",
},
PROCESSING: {
SUCCESS: "SUCCESS",
ERROR: "ERROR",
},
SUCCESS: {
RESET: "IDLE",
},
ERROR: {
RETRY: "PROCESSING",
RESET: "IDLE",
},
},
effects: {
PROCESSING: async ({ transition, condition }) => {
try {
// Запуск асинхронной операции
const processPromise = startLongProcess();
// Ожидание действия CANCEL до завершения операции
const raceResult = await Promise.race([
processPromise,
condition((action) => action.type === "CANCEL").then(() => ({ cancelled: true })),
]);
// Если операция была отменена
if (raceResult?.cancelled) {
transition({ type: "ERROR", payload: { error: "Operation cancelled" } });
return;
}
// Если операция завершилась успешно
transition({ type: "SUCCESS", payload: { result: raceResult } });
} catch (error) {
transition({ type: "ERROR", payload: { error: error.message } });
}
},
},
});createConfig
createConfig<C extends Record<string, Record<string, string | null>>>(
config: C
): CФункция для создания и типизации конфигурации автомата.
Параметры:
config: Объект конфигурации автомата.
Возвращает:
Тот же объект конфигурации, но с правильной типизацией.
Пример:
import { createConfig, createMachine } from "lite-fsm";
const config = createConfig({
IDLE: {
START: "RUNNING",
},
RUNNING: {
PAUSE: "PAUSED",
STOP: "IDLE",
},
PAUSED: {
RESUME: "RUNNING",
STOP: "IDLE",
},
});
const machine = createMachine({
config,
initialState: "IDLE",
// ...
});createReducer
createReducer<C, T, P>(
reducer: (
state: { state: State<C>; context: T },
action: P,
options: { nextState: State<C> | null; config: C }
) => { state: State<C>; context: T }
): typeof reducerФункция для создания и типизации пользовательского редьюсера.
Параметры:
reducer: Функция редьюсера.
Возвращает:
Тот же редьюсер, но с правильной типизацией.
Пример:
import { createReducer, createMachine } from "lite-fsm";
const reducer = createReducer((state, action, options) => {
const { state: currentState, context } = state;
const nextState = options.nextState || currentState;
switch (action.type) {
case "INCREMENT":
return {
state: nextState,
context: {
...context,
count: context.count + 1,
},
};
case "DECREMENT":
return {
state: nextState,
context: {
...context,
count: context.count - 1,
},
};
default:
return {
state: nextState,
context: {
...context,
...(action.payload || {}),
},
};
}
});
const machine = createMachine({
config: {
// ...
},
reducer,
// ...
});createEffect
createEffect<D extends DefaultDeps<any, any, any>, P extends Record<string, any> = {}>(
opts: {
type?: EffectType;
effect: MachineEffect<D, P>;
cancelFn?: (deps: D) => () => boolean;
}
): (deps: D, payload: P) => anyФункция для создания эффекта с возможностью отмены.
Параметры:
opts: Объект с опциями.type: (Опционально) Тип эффекта. Может принимать следующие значения:"every": (по умолчанию) Выполняет каждый вызов эффекта независимо от других вызовов."latest": Игнорирует вызовыtransitionиз предыдущих вызовов эффекта при новом вызове. Важно понимать, что код эффекта продолжает выполняться, но вызовыtransitionиз устаревших эффектов будут проигнорированы. Это позволяет избежать состояния гонки (race condition) в асинхронных операциях, например, при поиске или загрузке данных, когда важно обрабатывать только результат последнего запроса.
effect: Функция эффекта.cancelFn: (Опционально) Функция, которая возвращает функцию проверки условия отмены. Позволяет реализовать пользовательскую логику отмены эффекта.
Возвращает:
Функция эффекта с обработкой отмены.
Пример:
import { createEffect, createMachine } from "lite-fsm";
const searchMachine = createMachine({
config: {
IDLE: {
SEARCH: "SEARCHING",
},
SEARCHING: {
SEARCH_SUCCESS: "RESULTS",
SEARCH_ERROR: "ERROR",
},
RESULTS: {
SEARCH: "SEARCHING",
},
ERROR: {
SEARCH: "SEARCHING",
},
},
initialState: "IDLE",
initialContext: {
query: "",
results: [],
error: null,
isLoading: false,
},
effects: {
SEARCHING: createEffect({
type: "latest", // Обрабатывать только результаты последнего поискового запроса
effect: async ({ transition, services, action }) => {
try {
const query = action.payload?.query || "";
const results = await services.api.search(query);
// Этот transition будет проигнорирован, если был сделан более новый запрос
transition({
type: "SEARCH_SUCCESS",
payload: { results },
});
} catch (error) {
// Этот transition тоже будет проигнорирован для устаревших запросов
transition({
type: "SEARCH_ERROR",
payload: { error: error.message },
});
}
},
}),
},
});Wildcard
Специальный символ "*" используется для обозначения состояния или события, которое должно соответствовать любому значению.
Пример:
import { createMachine } from "lite-fsm";
const machine = createMachine({
config: {
IDLE: {
START: "RUNNING",
},
RUNNING: {
STOP: "IDLE",
},
// Обработка RESET из любого состояния
"*": {
RESET: "IDLE",
},
},
// ...
});React API
Библиотека lite-fsm предоставляет интеграцию с React через набор компонентов и хуков.
FSMContextProvider
<FSMContextProvider machineManager={manager}>{children}</FSMContextProvider>Компонент-провайдер, который делает менеджер автоматов доступным для всех дочерних компонентов через React Context.
Свойства:
machineManager: Объект менеджера автоматов, созданный с помощьюMachineManager.children: Дочерние компоненты.
Пример:
import { FSMContextProvider } from "lite-fsm/react";
import { MachineManager, createMachine } from "lite-fsm";
// Создание автомата
const counterMachine = createMachine({
config: {
IDLE: { INCREMENT: null, DECREMENT: null, RESET: null },
},
initialState: "IDLE",
initialContext: { count: 0 },
// ...
});
// Создание менеджера
const manager = MachineManager({
counter: counterMachine,
});
// Использование в приложении
function App() {
return (
<FSMContextProvider machineManager={manager}>
<Counter />
<Controls />
</FSMContextProvider>
);
}useSelector
useSelector<R>(
selector: (state: MachinesState<S>) => R,
equalityFn?: (oldValue: R, newValue: R) => boolean
): RХук для выбора и подписки на часть состояния из менеджера автоматов.
Параметры:
selector: Функция, которая принимает состояние всех автоматов и возвращает производную часть этого состояния.equalityFn: (Опционально) Функция сравнения для определения, изменилось ли выбранное состояние.
Возвращает: Выбранную часть состояния.
Пример:
import { useSelector } from "lite-fsm/react";
function Counter() {
// Получение значения счетчика
const count = useSelector((state) => state.counter.context.count);
// Получение нескольких значений с мемоизацией
const { isResettable, isDecrementable } = useSelector((state) => {
const count = state.counter.context.count;
return {
isResettable: count > 0,
isDecrementable: count > 0,
};
});
return (
<div>
<h2>Count: {count}</h2>
<p>Can reset: {isResettable ? "Yes" : "No"}</p>
<p>Can decrement: {isDecrementable ? "Yes" : "No"}</p>
</div>
);
}useTransition
useTransition<P extends FSMEvent<any, any> = any>(): (action: P) => voidХук, который возвращает функцию для отправки действий менеджеру автоматов.
Возвращает: Функция для отправки действий.
Пример:
import { useSelector, useTransition } from "lite-fsm/react";
function CounterControls() {
const count = useSelector((state) => state.counter.context.count);
const transition = useTransition();
return (
<div>
<button onClick={() => transition({ type: "INCREMENT" })}>Increment</button>
<button onClick={() => transition({ type: "DECREMENT" })} disabled={count <= 0}>
Decrement
</button>
<button onClick={() => transition({ type: "RESET" })} disabled={count === 0}>
Reset
</button>
</div>
);
}useManager
useManager<S extends Record<string, MachineConfig<any, any, any, any>>>(): MachineManager<S>Хук для получения прямого доступа к объекту менеджера автоматов.
Возвращает: Объект менеджера автоматов.
Пример:
import { useManager } from "lite-fsm/react";
import { useEffect } from "react";
function Debug() {
const manager = useManager();
// Получение полного состояния всех автоматов
const state = manager.getState();
useEffect(() => {
// Подписка на все изменения состояния
const unsubscribe = manager.onTransition((prevState, nextState, action) => {
console.log("Действие:", action);
console.log("Предыдущее состояние:", prevState);
console.log("Новое состояние:", nextState);
});
return unsubscribe;
}, [manager]);
return (
<div>
<h3>Текущее состояние:</h3>
<pre>{JSON.stringify(state, null, 2)}</pre>
<button onClick={() => manager.transition({ type: "RESET_ALL" })}>Сбросить все</button>
</div>
);
}