Event API
import { type Event, type EventCallable, createEvent } from "effector";
const event = createEvent();
Событие в effector представляет действие пользователя, шаг в процессе приложения, команду к выполнению или намерение внести изменения и многое другое.
Событие служит как точка входа в реактивный поток данных — простой способ сказать приложению “что-то произошло”.
Если вы не знакомы с событиями и способами работы с ними, то вам сюда Что такое события и как работать с ними.
Типы событий
Важно понять, что существуют два типа событий:
- Обычное событие, которое создается с помощью
createEvent
,.prepend
; эти события имеют типEventCallable
и могут быть вызваны, либо использованы вtarget
методаsample
. - Производное событие, который создается с помощью
.map
,.filter
,.filterMap
. Такие события имеют типEvent
и их нельзя вызывать или передавать вtarget
, effector сам вызовет их в нужном порядке, однако вы можете подписываться на эти события с помощьюsample
илиwatch
.
Интерфейс Event
Доступные методы и свойства событий:
Метод/Свойство | Описание |
---|---|
prepend(fn) | Создаёт новое событие EventCallable , трансформируют входные данные через fn и передает в исходное событие. |
map(fn) | Создаёт новое событие типа Event с результатом вызова fn после срабатывания исходного события. |
filter({fn}) | Создаёт новое событие типа Event , срабатывающий только если fn возвращает true . |
filterMap(fn) | Создаёт событие типа Event , срабатывающий с результатом fn , если тот не вернул undefined . |
watch(watcher) | Добавляет слушатель, вызывающий watcher при каждом срабатывании события. |
subscribe(observer) | Низкоуровневый метод для интеграции события со стандартным шаблоном Observable . |
sid | Уникальный идентификатор юнита (unit ). |
shortName | Свойство типа string , содержащее имя переменной, в которой объявлено событие. |
compositeName | Комплексное имя Event (включая домен и короткое имя) — удобно для логирования и трассировки. |
Методы событий
.prepend(fn)
Этот метод существует только для обычных событий (EventCallable
)! Это значит что этот метод может использоваться только на событиях созданных с помощью createEvent
.
Создает новое событие EventCallable
, который можно вызвать. При его срабатывании вызвает fn
и передает преобразованные данные в исходное событие.
- Формула
const second = first.prepend(fn);
- Тип
event.prepend<Before = void>(
fn: (_: Before) => Payload
): EventCallable<Before>
- Примеры
import { createEvent } from "effector";
// исходное событие
const userPropertyChanged = createEvent();
const changeName = userPropertyChanged.prepend((name) => ({
field: "name",
value: name,
}));
const changeRole = userPropertyChanged.prepend((role) => ({
field: "role",
value: role.toUpperCase(),
}));
userPropertyChanged.watch(({ field, value }) => {
console.log(`Свойство пользователя "${field}" изменилось на ${value}`);
});
changeName("john");
// => Свойство пользователя "name" изменилось на john
changeRole("admin");
// => Свойство пользователя "role" изменилось на ADMIN
changeName("alice");
// => Свойство пользователя "name" изменилось на alice
Вы можете считать этот метод функцией-обёрткой. Допустим, у нас есть функция с неидеальным API, но нам нужно часто её вызывать:
import { sendAnalytics } from "./analytics";
export function reportClick(item: string) {
const argument = { type: "click", container: { items: [arg] } };
return sendAnalytics(argument);
}
Это именно то, как работает .prepend()
:
import { sendAnalytics } from "./analytics";
export const reportClick = sendAnalytics.prepend((item: string) => {
return { type: "click", container: { items: [arg] } };
});
reportClick("example");
// reportClick сработал "example"
// sendAnalytics сработал с { type: "click", container: { items: ["example"] } }
- Детальное описание
Работает как обратный .map
. В случае .prepend
данные преобразуются до срабатывания исходного события, а в случае .map
данные преобразуются после срабатывания.
Если исходное событие принадлежит какому-либо домену, то новое событие также будет ему принадлежать.
- Возвращаемое значение
Возвращает новое событие EventCallable
.
Ознакомьтесь со всеми другими методами в Event.
.map(fn)
Создает новое производное событие, которое будет вызвано после того, как будет вызвано исходное событие, используя результат функции fn
в качестве его аргумента.
Функция fn
должна быть чистой.
- Формула
// Событие любого типа, как производное так и обычное
const first: Event<T> | EventCallable<T>;
const second: Event<F> = first.map(fn);
- Тип
event.map<T>(fn: (payload: Payload) => T): Event<T>
- Примеры
import { createEvent } from "effector";
const userUpdated = createEvent<{ name: string; role: string }>();
// вы можете разбить поток данных с помощью метода .map()
const userNameUpdated = userUpdated.map(({ user }) => name);
// либо преобразовать данные
const userRoleUpdated = userUpdated.map((user) => user.role.toUpperCase());
userNameUpdated.watch((name) => console.log(`Имя пользователя теперь [${name}]`));
userRoleUpdated.watch((role) => console.log(`Роль пользователя теперь [${role}]`));
userUpdated({ name: "john", role: "admin" });
// => Имя пользователя теперь [john]
// => Роль пользователя теперь [ADMIN]
- Детальное описание
Метод .map
позволяет вам разбивать и управлять потоком данных, а также извлекать или преобразовывать данные в рамках вашей модели бизнес-логики.
- Возвращаемое значение
Возвращает новое производное событие.
.filter({ fn })
sample
с аргументом filter
является предпочтительным методом фильтрации:
const event = createEvent();
const filteredEvent = sample({
clock: event,
filter: () => true,
});
Метод .filter
генерирует новое производное событие, которое будет вызвано после исходного события,в случае если функция fn
вернет true
. Эта специальная функция позволяет вам разбить поток данных на ветви и подписаться на них в рамках модели бизнес-логики.
Это очень удобно, если мы хотим на события которые срабатывают по условию.
- Формула
// Событие любого типа, как производное так и обычное
const first: Event<T> | EventCallable<T>;
const second: Event<T> = first.filter({ fn });
- Тип
event.filter(config: {
fn(payload: Payload): boolean
}): Event<Payload>
- Примеры
import { createEvent, createStore } from "effector";
const numbers = createEvent();
const positiveNumbers = numbers.filter({
fn: ({ x }) => x > 0,
});
const $lastPositive = createStore(0);
$lastPositive.on(positiveNumbers, (n, { x }) => x);
$lastPositive.watch((x) => {
console.log("последнее положительное:", x);
});
// => последнее положительное: 0
numbers({ x: 0 });
// нет реакции
numbers({ x: -10 });
// нет реакции
numbers({ x: 10 });
// => последнее положительное: 10
import { createEvent, createStore, sample } from "effector";
const numbers = createEvent();
const positiveNumbers = sample({
clock: numbers,
filter: ({ x }) => x > 0,
});
const $lastPositive = createStore(0);
$lastPositive.on(positiveNumbers, (n, { x }) => x);
$lastPositive.watch((x) => {
console.log("последнее положительное:", x);
});
// => последнее положительное: 0
numbers({ x: 0 });
// нет реакции
numbers({ x: -10 });
// нет реакции
numbers({ x: 10 });
// => последнее положительное: 10
- Возвращаемое значение
Возвращает новое производное событие.
.filterMap(fn)
Этот метод также можно заменить на операцию sample
с аргументами filter
+ fn
:
const event = createEvent();
const filteredAndMappedEvent = sample({
clock: event,
filter: () => true,
fn: () => "value",
});
Этот метод генерирует новое производное событие, которое может быть вызвано после исходного события, но с преобразованным аргументом. Этот специальный метод позволяет одновременно преобразовывать данные и фильтровать срабатывание события.
Этот метод наиболее полезен с API JavaScript, которые иногда возвращают undefined
.
- Формула
// Событие любого типа, как производное так и обычное
const first: Event<T> | EventCallable<T>;
const second: Event<F> = first.filterMap(fn);
- Тип
event.filterMap<T>(fn: (payload: Payload) => T | undefined): Event<T>
- Примеры
import { createEvent } from "effector";
const listReceived = createEvent<string[]>();
// Array.prototype.find() возвращает `undefined`, когда элемент не найден
const effectorFound = listReceived.filterMap((list) => {
return list.find((name) => name === "effector");
});
effectorFound.watch((name) => console.info("найден", name));
listReceived(["redux", "effector", "mobx"]); // => найден effector
listReceived(["redux", "mobx"]);
Функция fn
должна возвращать некоторые данные. Если возвращается undefined
, вызов производного события будет пропущено.
- Возвращаемое значение
Возвращает новое производное событие.
.watch(watcher)
Метод .watch
вызывается колбэк watcher
каждый раз при срабатывании события.
Метод watch
не обрабатывает и не сообщает о исключениях, не управляет завершением асинхронных операций и не решает проблемы гонки данных.
Его основное предназначение — для краткосрочного отладки и логирования.
- Формула
// Событие любого типа, как производное так и обычное
const event: Event<T> | EventCallable<T>;
const unwatch: () => void = event.watch(fn);
- Тип
event.watch(watcher: (payload: Payload) => any): Subscription
- Примеры
import { createEvent } from "effector";
const sayHi = createEvent();
const unwatch = sayHi.watch((name) => console.log(`${name}, привет!`));
sayHi("Питер"); // => Питер, привет!
unwatch();
sayHi("Дрю"); // => ничего не произошло
- Возвращаемое значение
Возвращает функцию для отмены подписки.
.subscribe(observer)
Это низкоуровневый метод для интеграции события со стандартным шаблоном Observable
.
Подробнее:
Вам не нужно использовать этот метод самостоятельно. Он используется под капотом движками рендеринга и так далее.
- Формула
const event = createEvent();
event.subscribe(observer);
- Тип
event.subscribe(observer: Observer<Payload>): Subscription
- Примеры
import { createEvent } from "effector";
const userLoggedIn = createEvent<string>();
const subscription = userLoggedIn.subscribe({
next: (login) => {
console.log("User login:", login);
},
});
userLoggedIn("alice"); // => User login: alice
subscription.unsubscribe();
userLoggedIn("bob"); // ничего не произойдет
Свойства
Этот набор свойств в основном задается с помощью effector/babel-plugin
или @effector/swc-plugin
. Таким образом, они существуют только при использовании Babel или SWC.
.sid
Это уникальный идентификатор для каждого события.
Важно отметить, что SID не изменяется при каждом запуске приложения, он статически записывается в пакет вашего приложения для абсолютной идентификации юнитов.
Это может быть полезно для отправки событий между рабочими или сервером/браузером: examples/worker-rpc.
- Тип
interface Event {
sid: string | null;
}
.shortName
Это свойство содержащее имя переменной, в которой объявлено событие.
import { createEvent } from "effector";
const demo = createEvent();
// demo.shortName === 'demo'
Но переопределение события в другую переменную ничего не изменит:
const another = demo;
// another.shortName === 'demo'
- Тип
interface Event {
shortName: string;
}
.compositeName
Это свойство содержит полную внутреннюю цепочку юнитов. Например, событие может быть создано доменом, поэтому составное имя будет содержать имя домена внутри него.
Обычно, если требуется длинное имя, лучше передать его явно в поле name
.
import { createEvent, createDomain } from "effector";
const first = createEvent();
const domain = createDomain();
const second = domain.createEvent();
console.log(first.compositeName);
// {
// "shortName": "first",
// "fullName": "first",
// "path": [
// "first"
// ]
// }
console.log(second.compositeName);
// {
// "shortName": "second",
// "fullName": "domain/second",
// "path": [
// "domain",
// "second"
// ]
// }
- Тип
interface Event {
compositeName: {
shortName: string;
fullName: string;
path: Array<string>;
};
}
Особенности Event
- В Effector любое событие поддерживает только один аргумент.
Вызов события с двумя или более аргументами, как в случае
someEvent(first, second)
, будет игнорировать все аргументы кроме первого. - В методах событий нельзя вызывать другие события или эффекты - функции должны быть чистыми
Связанные API и статьи
- API
createEvent
- Создание нового событияcreateApi
- Создание набора событий для стораmerge
- Слияние событий в одноsample
- Ключевой оператор для построения связей между юнитами
- Статьи
Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.
Помните, что переведенные статьи могут быть неактуальными, поэтому для получения наиболее точной и актуальной информации рекомендуем использовать оригинальную англоязычную версию документации.