Effect API
import { type Effect, createEffect } from "effector";
const effectFx = createEffect();
Эффект – это контейнер для сайд-эффектов, как синхронных, так и асинхронных. В комплекте имеет ряд заранее созданных событий и сторов, облегчающих стандартные действия. Является юнитом.
Эффекты можно вызывать как обычные функции (императивный вызов) а также подключать их и их свойства в различные методы api включая sample, и split (декларативное подключение).
Если вы не знакомы с эффектами и способами работы с ними, то вам сюда Асинхронность в effector с помощью эффектов.
Интерфейс Effect
Доступные методы и свойства событий:
Метод/Свойство | Описание |
---|---|
use(handler) | Заменяет обработчик эффекта на новую функцию handler . |
use.getCurrent() | Возвращает текущий обработчик эффекта. |
watch(watcher) | Добавляет слушатель, вызывающий watcher при каждом вызове эффекта. |
map(fn) | Создаёт новое производное событие, срабатывает при вызове эффекта с результатом вызова fn для параметров эффекта. |
prepend(fn) | Создаёт новое событие , трансформирующее входные данные через fn перед вызовом эффекта. |
filterMap(fn) | Создаёт новое производное событие, срабатывает при вызове эффекта с результатом fn , если тот не вернул undefined . |
done | Производное событие Event<{Params, Done}> , срабатывающее при успешном завершении эффекта. |
doneData | Производное событие Event<Done> с результатом успешного выполнения эффекта. |
fail | Производное событие Event<{Params, Fail}> , срабатывающее при ошибке выполнения эффекта. |
failData | Производное событие Event<Fail> с данными ошибки эффекта. |
finally | Производное событие Event<{Params, status, Done?, Fail?}> , срабатывающее при любом завершении эффекта. |
pending | Производный стор Store<boolean> со статусом выполнения эффекта (true во время выполнения). |
inFlight | Производный стор Store<number> с количеством активных вызовов эффекта. |
sid | Уникальный идентификатор юнита. |
shortName | Свойство типа string , содержащее имя переменной, в которой объявлен эффект. |
compositeName | Комплексное имя эффекта (включая домен и короткое имя) — удобно для логирования и трассировки. |
Особенности эффекта
- При императивном вызове всегда возвращают промис, отражающий ход выполнения сайд-эффекта.
- Эффекты принимают только один аргумент, как и события.
- Имеют встроенные сторы (
pending
,inFlight
) и события (done
,fail
,finally
и др.) для удобства работы.
Методы эффектов
.use(handler)
Если значение имплементации известно сразу, то оптимальнее использовать createEffect(handler)
.
Метод use(handler)
– это антипаттерн, который ухудшает вывод типов.
Определяет имплементацию эффекта: функцию, которая будет вызвана при срабатывании. Используется для случаев когда имплементация не установлена при создании или когда требуется изменение поведения эффекта при тестировании.
Принимает аргумент params
, который является данные, с которыми был вызван эффект.
Если на момент вызова эффект уже имел имплементацию, то она будет заменена на новую.
- Формула
const fx: Effect<Params, Done>;fx.use(handler);
- Тип
effect.use(handler: (params: Params) => Promise<Done> | Done): Effect< Params, Done, Fail>
- Примеры
import { createEffect } from "effector";
const fetchUserReposFx = createEffect();
fetchUserReposFx.use(async ({ name }) => { console.log("fetchUserReposFx вызван для github пользователя", name);
const url = `https://api.github.com/users/${name}/repos`; const req = await fetch(url); return req.json();});
await fetchUserReposFx({ name: "zerobias" });// => fetchUserReposFx вызван для github пользователя zerobias
- Возвращаемое значение
Возвращает текущий эффект.
.use.getCurrent()
Метод для получения текущей имплементации эффекта. Используется для тестирования.
Если у эффекта ещё не была установлена имплементация, то будет возвращена функция по умолчанию, при срабатывании она выбрасывает ошибку.
- Формула
const fx: Effect<Params, Done>;const handler = fx.use.getCurrent();
- Тип
effect.use.getCurrent(): (params: Params) => Promise<Done>
- Примеры
const handlerA = () => "A";const handlerB = () => "B";
const fx = createEffect(handlerA);
console.log(fx.use.getCurrent() === handlerA);// => true
fx.use(handlerB);console.log(fx.use.getCurrent() === handlerB);// => true
- Возвращаемое значение
Возвращает функцию-имплементацию эффекта, которая была установлена через createEffect или с помощью метода use.
.watch(watcher)
Вызывает дополнительную функцию с сайд-эффектами при каждом срабатывании эффекта. Не стоит использовать для логики, лучше заменить на sample
.
- Формула
const fx: Effect<Params, Done>;const unwatch = fx.watch(watcher);
- Тип
effect.watch(watcher: (payload: Params) => any): Subscription
- Примеры
import { createEffect } from "effector";
const fx = createEffect((params) => params);
fx.watch((params) => { console.log("эффект вызван с аргументом", params);});
await fx(10);// => эффект вызван с аргументом 10
- Возвращаемое значение
Функция отмены подписки, после её вызова watcher
перестаёт получать обновления и удаляется из памяти.
.map(fn)
Метод map
создает производное событие. Событие вызывается в момент выполнения эффекта, с теми же аргументами, что и у эффекта, и результатом, возвращаемым функцией fn
. Работает по аналогии с Event.map(fn)
.
- Формула
const fx: Effect<Params, Done>;const eventB = fx.map(fn);
- Тип
effect.map<T>(fn: (params: Params) => T): Event<T>
- Примеры
import { createEffect } from "effector";
interface User { // ...}
const saveUserFx = createEffect(async ({ id, name, email }: User) => { // ... return response.json();});
const userNameSaving = saveUserFx.map(({ name }) => { console.log("Начинаем сохранение пользователя: ", name); return name;});
const savingNotification = saveUserFx.map(({ name, email }) => { console.log("Оповещение о сохранении"); return `Сохранение пользователя: ${name} (${email})`;});
// При вызове эффекта сработают оба производных событияawait saveUserFx({ id: 1, name: "Иван", email: "ivan@example.com" });// => Начинаем сохранение пользователя: Иван// => Сохранение пользователя: Иван (ivan@example.com)
- Возвращаемое значение
Возвращает новое производное событие.
.prepend(fn)
Создаёт новое событие для преобразования данных перед запуском эффекта. По сравнению с map, работает в обратном направлении. Работает по аналогии с Event.prepend(fn)
.
- Формула
const fx: Effect<Params, Done>;const trigger = fx.prepend(fn);
- Тип
effect.prepend<Before>(fn: (_: Before) => Params): EventCallable<Before>
- Примеры
import { createEffect } from "effector";
const saveFx = createEffect(async (data) => { console.log('saveFx вызван с: 'data) await api.save(data);});
// создаем событие-триггер для эффектаconst saveForm = saveFx.prepend((form) => ({ ...form, modified: true}));
saveForm({ name: "John", email: "john@example.com" });// => saveFx вызван с : { name: "John", email: "john@example.com", modified: true }
- Возвращаемое значение
Возвращает новое событие.
.filterMap(fn)
Метод filterMap
создаёт производное событие. Вычисление функции fn
запускается одновременно с эффектом, однако если функция возвращает undefined
, событие не срабатывает. Работает аналогично методу .map(fn)
, но с фильтрацией по возвращаемому значению.
- Формула
const fx: Effect<Params, Done>;const filtered = fx.filterMap(fn);
- Тип
effect.filterMap<T>(fn: (payload: Params) => T | undefined): Event<T>
- Примеры
import { createEffect } from "effector";
const validateAndSaveFx = createEffect(async (userData) => { if (!userData.isValid) { throw new Error("Invalid data"); }
return await saveToDatabase(userData);});
// Создаем событие только для валидных данныхconst validDataProcessing = validateAndSaveFx.filterMap((userData) => { if (userData.isValid && userData.priority === "high") { return { id: userData.id, timestamp: Date.now(), }; } // Если данные не валидны или приоритет не высокий, событие не сработает});
validDataProcessing.watch(({ id, timestamp }) => { console.log(`Обработка высокоприоритетных данных ID: ${id} в ${timestamp}`);});
// Примеры вызововawait validateAndSaveFx({ id: 1, isValid: true, priority: "high", role: "user",});// => Обработка высокоприоритетных данных ID: 1 в 1703123456789
- Возвращаемое значение
Возвращает новое производное событие.
Свойства эффектов
.done
Производное событие, которое срабатывает с результатом выполнения эффекта и аргументом, переданным при вызове.
- Тип
interface Effect<Params, Done> { done: Event<{ params: Params; result: Done }>;}
- Примеры
import { createEffect } from "effector";
const fx = createEffect((value) => value + 1);
fx.done.watch(({ params, result }) => { console.log("Вызов с аргументом", params, "завершён со значением", result);});
await fx(2);// => Вызов с аргументом 2 завершён со значением 3
.doneData
Производное событие, которое срабатывает с результатом успешного выполнения эффекта.
- Тип
interface Effect<any, Done> { doneData: Event<Done>;}
- Примеры
import { createEffect } from "effector";
const fx = createEffect((value) => value + 1);
fx.doneData.watch((result) => { console.log(`Эффект успешно выполнился, вернув ${result}`);});
await fx(2);// => Эффект успешно выполнился, вернув 3
.fail
Производное событие, которое срабатывает с ошибкой, возникшей при выполнении эффекта и аргументом, переданным при вызове.
- Тип
interface Effect<Params, any, Fail> { fail: Event<{ params: Params; error: Fail }>;}
- Примеры
import { createEffect } from "effector";
const fx = createEffect(async (value) => { throw new Error(value - 1);});
fx.fail.watch(({ params, error }) => { console.log("Вызов с аргументом", params, "завершился с ошибкой", error.message);});
fx(2);// => Вызов с аргументом 2 завершился с ошибкой 1
.failData
Производное событие, которое срабатывает с ошибкой, возникшей при выполнении эффекта.
- Тип
interface Effect<any, any, Fail> { failData: Event<Fail>;}
- Примеры
import { createEffect } from "effector";
const fx = createEffect(async (value) => { throw new Error(value - 1);});
fx.failData.watch((error) => { console.log(`Вызов завершился с ошибкой ${error.message}`);});
fx(2);// => Вызов завершился с ошибкой 1
.finally
Производное событие, которое срабатывает как при успехе, так и в случае ошибки завершении эффекта с подробной информацией об аргументах, результатах и статусе выполнения.
- Тип
interface Effect<Params, Done, Fail> { finally: Event< | { status: "done"; params: Params; result: Done; } | { status: "fail"; params: Params; error: Fail; } >;}
- Примеры
import { createEffect } from "effector";
const fetchApiFx = createEffect(async ({ time, ok }) => { await new Promise((resolve) => setTimeout(resolve, time));
if (ok) { return `${time} ms`; }
throw Error(`${time} ms`);});
fetchApiFx.finally.watch((value) => { switch (value.status) { case "done": console.log("Вызов с аргументом", value.params, "завершён со значением", value.result); break; case "fail": console.log("Вызов с аргументом", value.params, "завершён с ошибкой", value.error.message); break; }});
await fetchApiFx({ time: 100, ok: true });// => Вызов с аргументом {time: 100, ok: true} завершён со значением 100 ms
fetchApiFx({ time: 100, ok: false });// => Вызов с аргументом {time: 100, ok: false} завершён с ошибкой 100 ms
.pending
Производный стор, который показывает, что эффект находится в процессе выполнения.
- Тип
interface Effect<any, any> { pending: Store<boolean>;}
- Детальное описание
Это свойство избавляет от необходимости писать подобный код:
const $isRequestPending = createStore(false) .on(requestFx, () => true) .on(requestFx.done, () => false) .on(requestFx.fail, () => false);
- Примеры
import React from "react";import { createEffect } from "effector";import { useUnit } from "effector-react";
const fetchApiFx = createEffect(async (ms) => { await new Promise((resolve) => setTimeout(resolve, ms));});
fetchApiFx.pending.watch(console.log);// => false
const App = () => { const loading = useUnit(fetchApiFx.pending); return <div>{loading ? "Загрузка..." : "Загрузка завершена"}</div>;};
fetchApiFx(1000);// => true// => false
.inFlight
Производный стор, который показывает число запущенных эффектов, которые находятся в процессе выполнения. Может использоваться для ограничения числа одновременных запросов.
- Тип
interface Effect<any, any> { inFlight: Store<number>;}
- Детальное описание
Это свойство избавляет от необходимости писать подобный код:
const $requestsInFlight = createStore(0) .on(requestFx, (n) => n + 1) .on(requestFx.done, (n) => n - 1) .on(requestFx.fail, (n) => n - 1);
- Примеры
import { createEffect } from "effector";
const fx = createEffect(async () => { await new Promise((resolve) => setTimeout(resolve, 500));});
fx.inFlight.watch((amount) => { console.log("выполняется запросов:", amount);});// => выполняется запросов: 0
const req1 = fx();// => выполняется запросов: 1
const req2 = fx();// => выполняется запросов: 2
await Promise.all([req1, req2]);
// => выполняется запросов: 1// => выполняется запросов: 0
.sid
Уникальный идентификатор юнита. Важно отметить, что SID не изменяется при каждом запуске приложения, он статически записывается в пакет вашего приложения для абсолютной идентификации юнитов. Задаётся автоматически через Babel plugin.
- Тип
interface Effect<any, any> { sid: string | null;}
.shortName
Свойство типа string
, содержащее имя переменной, в которой объявлен эффект. Имя эффекта. Задаётся либо явно, через поле name
в createEffect, либо автоматически через babel plugin.
- Тип
interface Effect<any, any> { shortName: string;}
.compositeName
Комплексное имя эффекта (включая домен и короткое имя) — удобно для логирования и трассировки.
- Тип
interface Effect<any, any> { compositeName: { shortName: string; fullName: string; path: Array<string>; };}
- Примеры
import { createEffect, createDomain } from "effector";
const first = createEffect();const domain = createDomain();const second = domain.createEffect();
console.log(first.compositeName);// {// "shortName": "first",// "fullName": "first",// "path": [// "first"// ]// }
console.log(second.compositeName);// {// "shortName": "second",// "fullName": "domain/second",// "path": [// "domain",// "second"// ]// }
Связанные API и статьи
- API
createEffect
- Создание нового эффектаEvent API
- Описание событий, его методов и свойствStore API
- Описание сторов, его методов и свойствsample
- Ключевой оператор для построения связей между юнитамиattach
- Создает новые эффекты на основе других эффектов
- Статьи
Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.
Помните, что переведенные статьи могут быть неактуальными, поэтому для получения наиболее точной и актуальной информации рекомендуем использовать оригинальную англоязычную версию документации.