От RTK listenerMiddleware
listenerMiddleware из @reduxjs/toolkit — это то, что в экосистеме Redux ближе всего к триггеру. Миграция в основном механическая: одна регистрация startListening({ actionCreator, effect }) становится одним createTrigger({ id, events, handler }). Codemod migrate-from-listener-middleware обрабатывает форму с actionCreator; другие формы требуют человеческой ревизии.
Соответствие ментальных моделей
Заголовок раздела «Соответствие ментальных моделей»| listenerMiddleware | Triggery |
|---|---|
startListening({ actionCreator: foo, effect }) | createTrigger({ events: ['foo'], handler }) |
startListening({ matcher: isAnyOf(a, b) }) | events: ['a', 'b'] и ветвление по event.name |
startListening({ predicate: (action, state) => … }) | Предикат → условие + охранник в обработчике |
effect(action, api) — api.getState() | conditions.someName (зарегистрировано через useReduxCondition) |
api.dispatch(action) | actions.someAction?.(payload) |
api.cancelActiveListeners() | concurrency: 'take-latest' (по умолчанию) с signal |
api.signal | signal в контексте обработчика |
api.fork(task) | Под-обработчик; явная стратегия конкурентности take-every |
Паттерн 1 — listener на основе actionCreator
Заголовок раздела «Паттерн 1 — listener на основе actionCreator»Доступ к состоянию (api.getState().settings) становится useReduxCondition, зарегистрированным фичей settings; dispatch тоста становится useAction в UI-фиче.
Паттерн 2 — matcher
Заголовок раздела «Паттерн 2 — matcher»Перечисление обоих имён в events: индексирует триггер под оба ключа событий — обработчик видит discriminated union полезных нагрузок.
Паттерн 3 — предикат против состояния
Заголовок раздела «Паттерн 3 — предикат против состояния»Проверка предиката “состояние верное?” превращается в check.is внутри обработчика — та же логика, ленивая оценка из единого источника.
Паттерн 4 — race против signal (асинхронные listener’ы)
Заголовок раздела «Паттерн 4 — race против signal (асинхронные listener’ы)»take-latest — это значение по умолчанию; concurrency: передаётся только для опт-ина в что-то другое.
Codemod
Заголовок раздела «Codemod»@triggery/codemod поставляет migrate-from-listener-middleware для формы с actionCreator:
Для каждого вызова startListening({ actionCreator, effect }) codemod генерирует один *.trigger.ts-заготовку с телом effect, перенесённым дословно в обработчик, и маркером // TODO для конвертации getState()/dispatch(). Формы с matcher / predicate определяются, но пропускаются — их делаешь руками по паттернам выше.
Оставить Redux
Заголовок раздела «Оставить Redux»Удалять Redux не обязательно. Самый частый путь: оставить стор, оставить createSlice и заменить listenerMiddleware как механизм побочных эффектов на триггеры. Адаптер @triggery/redux показывает селекторы как условия в 30 строках — см. @triggery/redux.