Глобальные переходы
Глобальные переходы позволяют определить события, которые могут обрабатываться независимо от текущего состояния автомата. Они подходят для общих событий: ошибок, сброса состояния или действий пользователя, доступных из любой точки процесса.
Примеры reducer-ов на этой странице написаны в стиле Immer: они мутируют draft
stateи предполагают, чтоMachineManagerсоздан сimmerMiddleware. Без middleware reducer должен вернуть новый объект{ state, context }.
Концепция глобальных переходов
В lite-fsm глобальные переходы задаются ключом "*". Такой переход используется как резервный вариант, если в текущем состоянии нет перехода для того же события.
Синтаксис определения глобальных переходов
Глобальные переходы определяются в конфигурации автомата с использованием символа "*" в качестве ключа:
import { createMachine } from "@lite-fsm/core";
export const player = createMachine({
config: {
"*": {
CHANGE_TRACK: "LOAD_TRACK_DATA_PENDING",
},
IDLE: {},
LOAD_TRACK_DATA_PENDING: {},
SELECT_STREAM_PENDING: {},
SET_STREAM_PENDING: {},
},
initialState: "IDLE",
initialContext: {},
reducer: (state, _action, { nextState }) => {
state.state = nextState;
},
});Приоритет переходов
При обработке событий lite-fsm проверяет переходы в следующем порядке:
- Специфичные переходы для текущего состояния
- Глобальные переходы (если специфичный переход не определен)
Это означает, что глобальные переходы могут быть переопределены в конкретных состояниях:
config: {
"*": {
RESET: "IDLE", // Применяется ко всем состояниям
ERROR: "ERROR_STATE", // Применяется ко всем состояниям, кроме тех, где переопределено
},
PLAYING: {
ERROR: "PLAYING_ERROR", // Переопределяет глобальный переход для состояния PLAYING
},
}Применение глобальных переходов
1. Общие действия, доступные в любом состоянии
const player = createMachine({
config: {
"*": {
CHANGE_TRACK: "PAUSED",
},
PAUSED: {
RESUME: "PLAYING",
},
PLAYING: {
PAUSE: "PAUSED",
},
},
initialState: "PAUSED",
initialContext: {},
});2. Обработка ошибок на глобальном уровне
const app = createMachine({
config: {
"*": {
NETWORK_ERROR: "ERROR",
FATAL_ERROR: "CRASHED",
},
IDLE: {
FETCH_DATA: "LOADING",
},
LOADING: {
DATA_LOADED: "DATA_READY",
},
DATA_READY: {
PROCESS_DATA: "PROCESSING",
},
PROCESSING: {},
ERROR: {},
CRASHED: {},
},
initialState: "IDLE",
initialContext: {},
});3. Сброс состояния
const form = createMachine({
config: {
"*": {
RESET_FORM: "INITIAL", // Сброс формы из любого состояния
},
INITIAL: {
FIELD_CHANGE: "EDITING",
},
EDITING: {
SUBMIT: "VALIDATING",
},
VALIDATING: {
VALIDATION_SUCCESS: "SUBMITTING",
VALIDATION_ERROR: "INVALID",
},
INVALID: {
FIELD_CHANGE: "EDITING",
},
SUBMITTING: {
SUBMIT_SUCCESS: "SUCCESS",
SUBMIT_ERROR: "ERROR",
},
SUCCESS: {},
ERROR: {
RETRY: "SUBMITTING",
},
},
initialState: "INITIAL",
initialContext: {},
});Лучшие практики
- Используйте для общих операций: Глобальные переходы подходят для действий, которые должны быть доступны из любого состояния.
- Избегайте избыточности: Не дублируйте один и тот же переход во всех состояниях, используйте глобальный переход.
- Переопределяйте где нужно: В состояниях, где поведение должно отличаться, переопределите глобальный переход.
- Документируйте глобальные события: явно указывайте, какие события обрабатываются глобально, чтобы поведение оставалось проверяемым.
- Используйте для отладки: Глобальные переходы удобны для добавления отладочных действий во время разработки.
- Ограничивайте область применения: слишком много глобальных переходов усложняет чтение графа. Чаще всего они уместны для сброса, отмены или критических ошибок.