Event (событие, эвент) это декларация намерения сделать что-либо: запустить вычисления, отправить сообщение другой секции приложения или обновить состояния в приложении. Одна из основных управляющих сущностей, при срабатывании запускает цепочки реактивных вычислений в приложении. Является юнитом
События можно вызывать как обычные функции (императивный вызов) а также подключать в различные методы api включая sample, guard и split (декларативное подключение). При императивном вызове принимают максимум один аргумент и всегда возвращают переданные данные
Методы
map(fn)
Создает производное событие на основе данных из исходного
Формула
declare const eventA: Event<T>
const eventB = eventA.map(/*fn*/(data: T) => S)
-> Event<S>
При вызове исходного события eventA
, функция-обработчик fn
будет вызвана с поступившими данными, после чего производный эвент eventB
будет вызван с результатом вычислений
eventA -> fn -> eventB
Аргументы
fn
:(data: T) => S
Функция-обработчик, которая будет вычислять данные для передачи в производное событие
eventB
на основе данных из исходного эвентаeventA
. Должна быть чистойАргументы
data
: Данные с которыми сработало исходное событиеeventA
Возвращает
Данные для передачи в производное событие
eventB
Возвращает
Новое, производное событие
Пример
import { createEvent } from "effector";
const updateUser = createEvent();
const userNameUpdated = updateUser.map(({ name }) => name);
const userRoleUpdated = updateUser.map(({ role }) => role.toUpperCase());
userNameUpdated.watch((name) => console.log(`Имя пользователя изменено на ${name}`));
userRoleUpdated.watch((role) => console.log(`Роль пользователя изменена на ${role}`));
updateUser({ name: "john", role: "admin" });
// => Имя пользователя изменено на john
// => Роль пользователя изменена на ADMIN
prepend(fn)
Создаёт событие-триггер для преобразования данных перед запуском исходного эвента. По сравнению с map, работает в обратном направлении
Формула
declare const targetEvent: Event<S>
const trigger = targetEvent.prepend(/*fn*/(data: T) => S)
-> Event<T>
При вызове события trigger
, функция-обработчик fn
будет вызвана с поступившими данными, после чего эвент targetEvent
будет вызван с результатом вычислений
trigger -> fn -> targetEvent
Аргументы
fn
:(data: T) => S
Функция-обработчик, которая будет вычислять данные для передачи в исходное событие
targetEvent
на основе данных эвентаtrigger
. Должна быть чистойАргументы
data
: Данные с которыми сработало событиеtrigger
Возвращает
Данные для передачи в исходное событие
targetEvent
Возвращает
Новое событие
Пример
Пример использования
import { createEvent } from "effector";
const userPropertyChanged = createEvent();
userPropertyChanged.watch(({ field, value }) => {
console.log(`Свойство пользователя "${field}" изменено на ${value}`);
});
const changeName = userPropertyChanged.prepend((name) => ({
field: "name",
value: name,
}));
const changeRole = userPropertyChanged.prepend((role) => ({
field: "role",
value: role.toUpperCase(),
}));
changeName("john");
// => Свойство пользователя "name" изменено на john
changeRole("admin");
// => Свойство пользователя "role" изменено на ADMIN
changeName("alice");
// => Свойство пользователя "name" изменено на alice
filterMap(fn)
Создает производное событие на основе данных из исходного с возможностью отмены вызова
Метод добавлен в effector 20.0.0
Формула
declare const eventA: Event<T>
const eventB = eventA.filterMap(
/*fn*/ (data: T) => S | void
)
-> Event<S>
При вызове исходного события eventA
, функция-обработчик fn
будет вызвана с поступившими данными, после чего, если функция вернула не undefined, производный эвент eventB
будет вызван с результатом вычислений
eventA -> fn -> eventB
Если требуется только фильтрация вызовов без трансформации данных, то оптимальнее использовать guard
Аргументы
fn
:(data: T) => S | void
Функция-обработчик, которая будет вычислять данные для передачи в производное событие
eventB
на основе данных из исходного эвентаeventA
. Должна быть чистойАргументы
data
: Данные с которыми сработало исходное событиеeventA
Возвращает
Данные для передачи в производное событие
eventB
либо undefined, если вызовeventB
не требуется
Возвращает
Новое, производное событие
Пример
Использование с методами JavaScript возвращающими undefined
const listReceived = createEvent<string[]>()
const effectorFound = listReceived.filterMap(list => list.find(name => name === 'effector'))
effectorFound.watch(name => console.info("found", name))
listReceived(["redux", "effector", "mobx"]) // found effector
listReceived(["redux", "mobx"])
Использование c nullable React ref
Методы modal.showModal и modal.close – стандартные возможности dom-элемента <dialog>
Статья в MDN про showModal
import React from "react";
import { createEvent, createStore } from "effector";
const openModal = createEvent();
const closeModal = createEvent();
const openModalUnboxed = openModal.filterMap((ref) => {
if (ref.current) return ref.current;
});
const closeModalUnboxed = closeModal.filterMap((ref) => {
if (ref.current) return ref.current;
});
openModalUnboxed.watch((modal) => modal.showModal());
closeModalUnboxed.watch((modal) => modal.close());
const App = () => {
const modalRef = React.useRef(null);
return (
<>
<dialog ref={modalRef}>
<form method="dialog">
<fieldset>
<legend>Модальное окно</legend>
Нажмите для закрытия
<button onSubmit={() => closeModal(modalRef)} type="submit">
❌
</button>
</fieldset>
</form>
</dialog>
<button onClick={() => openModal(modalRef)}>Открыть модальное окно</button>
</>
);
};
filter({fn})
Создает производное событие с возможностью отмены вызова
Более гибким способом фильтрации является sample, рекомендуется использовать именно его.
Формула
declare const eventA: Event<T>
const eventB = eventA.filter(/*config*/ {fn: (data: T) => boolean})
-> Event<T>
При вызове исходного события eventA
, функция-обработчик fn
будет вызвана с поступившими данными, после чего, если функция вернула истинное значение, производный эвент eventB
будет вызван с теми же данными
Аргументы
config
: Объект конфигурацииfn
:(data: T) => boolean
Функция-предикат, которая определяет необходимость вызова производного события
eventB
возвращая истинное значение,должна быть чистой
Возвращает
Новое, производное событие
Объектная форма аргумента используется потому что event.filter(fn) был сокращённой формой filterMap
Пример
import { createEvent, createStore } from "effector";
const numbers = createEvent();
const positiveNumbers = numbers.filter({
fn: ({ x }) => x > 0,
});
const $lastPositive = createStore(0).on(positiveNumbers, (n, { x }) => x);
$lastPositive.watch((x) => {
console.log("последнее положительное значение:", x);
});
// => последнее положительное значение: 0
numbers({ x: 0 });
// ничего не произошло
numbers({ x: -10 });
// ничего не произошло
numbers({ x: 10 });
// => последнее положительное значение: 10
watch(watcher)
Вызывает функцию с сайд-эффектами при каждом срабатывании события
Формула
declare const event: Event<T>
event.watch(/*watcher*/ (data: T) => any)
-> Subscription
Аргументы
watcher
:(data: T) => any
Функция с сайд-эффектами, в качестве первого аргумента получает значение с которым было вызвано событие. Возвращаемое значение не используется
Возвращает
Subscription: Функция отмены подписки, после её вызова watcher
перестаёт получать обновления и удаляется из памяти. Повторные вызовы функции отмены подписки не делают ничего
Пример
import { createEvent } from "effector";
const sayHi = createEvent();
const stop = sayHi.watch((name) => {
console.log(`Привет, ${name}!`);
});
sayHi("Боб");
// => Привет, Боб!
stop();
sayHi("Алиса");
// => ничего не произошло
Свойства
shortName
Имя события. Задаётся либо явно, через поле name
в createEvent, либо автоматически через Babel plugin. Используется для обработки сущностей программно, например при использовании хуков домена
Формула
declare const event: Event<any>
event.shortName
-> string
sid
Стабильный идентификатор события. Задаётся автоматически через Babel plugin
Формула
declare const event: Event<any>
event.sid
-> string | null
Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.
Помните, что переведенные статьи могут быть неактуальными, поэтому для получения наиболее точной и актуальной информации рекомендуем использовать оригинальную англоязычную версию документации.