Skip to content

Реакции

Реакция — это правило модели. Она не хранит состояние, не сообщает о факте и не выполняет внешнюю работу. Ее задача — связать эти части: событие произошло, стор изменился, эффект завершился, значит модель должна выполнить правило.

Чаще всего реакция живет рядом с теми юнитами, которые связывает. Так по модели видно не только “какие данные есть”, но и “почему они меняются”.

ts
const queryChanged = event<string>();
const searchSubmitted = event<void>();

const query = store("");
const results = store({ items: [] as string[] });

const searchFx = effect(async (text: string) => {
  const response = await fetch(`/api/search?q=${encodeURIComponent(text)}`);
  return (await response.json()) as string[];
});

reaction({
  on: queryChanged,
  run(text) {
    query.value = text;
  },
});

reaction({
  on: searchSubmitted,
  run() {
    void searchFx(query.value);
  },
});

reaction({
  on: searchFx.doneData,
  run(items) {
    results.items = items;
  },
});

Здесь события остаются маленькими: они только называют произошедшее. Эффект занимается запросом. Сторы помнят состояние. Реакции описывают причинность между ними.

Автоматические зависимости

По умолчанию удобно начинать с реакции без on. Внутри такой реакции вы читаете сторы, а Virentia запоминает, от каких сторов зависит правило. Когда один из прочитанных сторов меняется, реакция запускается снова в том же scope.

ts
const query = store("");
const online = store(true);
const canSearch = store(false);

reaction(() => {
  canSearch.value = online.value && query.value.trim().length > 2;
});

Этот режим особенно полезен, когда зависимости проще выразить чтением состояния, а не списком источников. Если внутри есть ветвление, список зависимостей обновляется после каждого запуска: реакция будет слушать именно те сторы, которые были прочитаны в актуальной ветке.

Если значение полностью выводится из других сторов и не требует записи в отдельный стор, сначала посмотрите на computed. Реакция нужна, когда правило должно выполнить действие: записать состояние, вызвать эффект, отправить событие или синхронизироваться с внешним кодом.

Явный on

on нужен, когда важна причина запуска: конкретное событие, эффект или юнит жизненного цикла эффекта. В этом режиме реакция не запускается при создании. Она срабатывает только от указанного юнита и получает его payload.

ts
reaction({
  on: messageReceived,
  run(message) {
    messages.items = [...messages.items, message];
  },
});

Используйте явный on, когда payload является частью правила. Например, “пришло сообщение”, “форма отправлена”, “запрос успешно завершился”, “эффект был отменен”. Это читается лучше, чем реакция, которая просто наблюдает за состоянием и пытается угадать, что произошло.

Можно слушать несколько источников, если правило для них действительно одинаковое:

ts
reaction({
  on: [saved, cancelled],
  run() {
    modalOpened.value = false;
  },
});

Остановка

Реакция возвращает объект со stop(). После остановки она отвязывается от зависимостей и больше не получает новые запуски.

ts
const subscription = reaction({
  on: ticked,
  run() {
    count.value += 1;
  },
});

subscription.stop();

В динамических моделях чаще не нужно вызывать stop() вручную. Создавайте такие реакции внутри owner: при dispose Virentia отвяжет их вместе с остальной временной работой.