От useEffect
useEffect — это универсальный escape hatch React, и большинство приложений в итоге используют его для задач, под которые он не был спроектирован: оркестрация побочных эффектов через границы фич. Эта страница — полевой справочник по переводу таких useEffect в Triggery, по одной форме за раз.
Соответствие ментальных моделей
Заголовок раздела «Соответствие ментальных моделей»Форма useEffect | Аналог в Triggery |
|---|---|
| Тело эффекта | Тело обработчика |
useEffect(fn, [deps]) с перезапуском при изменении зависимостей | Геттер useCondition — читается лениво, без перезапуска |
return () => cleanup() | signal.addEventListener('abort', …) для асинхронных обработчиков; синхронным cleanup не нужен |
setTimeout внутри эффекта | actions.debounce(ms).foo(p) — таймером владеет рантайм |
| Подписка на внешний источник | useDomEvent / useSocketIoEvent / useWebSocketEvent из адаптеров |
if (cond) doThing() внутри эффекта | check.is('cond', …) внутри обработчика |
| Вызов action из Zustand/Redux/Jotai | actions.someAction?.(payload) |
Паттерн 1 — useEffect с cleanup (подписка / отписка)
Заголовок раздела «Паттерн 1 — useEffect с cleanup (подписка / отписка)»Адаптер @triggery/socket сам берёт на себя on / off и даёт fireMessage стабильную идентичность. Обратные вызовы, захваченные в замыкания, исчезают.
Паттерн 2 — useEffect, гоняющийся за зависимостями
Заголовок раздела «Паттерн 2 — useEffect, гоняющийся за зависимостями»Массив зависимостей раздувается; пропущенная запись — это тихий баг. В Triggery правило живёт в одном обработчике, а провайдеры никого не перерендеривают:
Каждое значение, которое читает обработчик, приходит из useCondition где-то ещё — и ни один из этих компонентов не перерендеривается при срабатывании события.
Паттерн 3 — useEffect, вызывающий action стора
Заголовок раздела «Паттерн 3 — useEffect, вызывающий action стора»Стор не протекает в файл триггера — реактор, который владеет состоянием badge, владеет и dispatch’ем.
Паттерн 4 — useEffect, слушающий сокет
Заголовок раздела «Паттерн 4 — useEffect, слушающий сокет»Два слушателя, два cleanup’а, два массива зависимостей, одно событие. Заменяется на один fireMessage и много useAction-реакторов — полная версия в рецепте notification pipeline.
Codemod
Заголовок раздела «Codemod»@triggery/codemod поставляет extract-trigger, который вытаскивает первый useEffect(() => { … }, []) из файла в соседний *.trigger.ts-заготовку и переписывает компонент на вызов useEvent(...). Он намеренно механический — типизация схемы, вынос условий и логика cleanup остаются на тебе.
Для всего более сложного (эффекты с условными зависимостями, несколько эффектов, делящих состояние, кастомные хуки) codemod не помогает — но паттерны выше говорят, что делать руками.
Когда оставить useEffect
Заголовок раздела «Когда оставить useEffect»Triggery — не замена каждому useEffect. Оставляй его, когда:
- Побочный эффект чисто локален в одном компоненте и не вырастет (“переключить класс по клику”).
- Ты подписываешься на браузерное API, которое интересно только этому компоненту (например,
ResizeObserver, чей результат рендерится тут же). - Ты синхронизируешься с ref (
ref.current?.focus()после изменения состояния).
Всё, что пересекает границу фичи, — это то, для чего существует Triggery.