Effect (эффект) это контейнер для сайд-эффектов, возможно асинхронных. В комплекте имеет ряд заранее созданных эвентов и сторов, облегчающих стандартные действия. Является юнитом
Эффекты можно вызывать как обычные функции (императивный вызов) а также подключать их и их свойства в различные методы api включая sample, guard и split (декларативное подключение). При императивном вызове принимают максимум один аргумент и всегда возвращают промис, отражающий ход выполнения сайд-эффекта
Методы
use(handler)
Определяет имплементацию эффекта: функцию, которая будет вызвана при срабатывании. Используется для случаев когда имплементация не установлена при создании или когда требуется изменение поведения эффекта при тестировании
Если на момент вызова эффект уже имел имплементацию, то она будет заменена на новую
Нужно предоставить имплементацию либо через use, либо через createEffect, иначе при вызове эффекта возникнет ошибка “no handler used in %effect name%“
Формула
declare const fx: Effect<T, S>;
fx.use(/*handler*/ (params: T) => S | Promise<S>);
Аргументы
-
handler
:(params: T) => S | Promise<S>
Функция-имплементация эффекта. Может быть как синхронной, так и асинхронной
Аргументы
params
: Данные, с которыми был вызван эффект
Возвращает
Результат выполнения эффекта в виде значения, либо в виде промиса со значением
Возвращает
Текущий эффект
Если значение имплементации известно сразу, то оптимальнее использовать createEffect(handler)
createEffect().use(handler)
это антипаттерн, который ухудшает вывод типов
Пример
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()
Метод для получения текущей имплементации эффекта. Используется для тестирования
Если у эффекта ещё не была установлена имплементация, то будет возвращена функция по умолчанию, при срабатывании она выбрасывает ошибку
Формула
declare const fx: Effect<P, D>
fx.use.getCurrent()
-> (params: P) => D
Возвращает
Функцию-имплементацию эффекта, которая была установлена через createEffect или с помощью метода use
Пример
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
watch(watcher)
Вызывает дополнительную функцию с сайд-эффектами при каждом срабатывании эффекта
По мере усложнения логики проекта оптимальнее заменить на комбинацию дополнительного эффекта и sample
Формула
declare const fx: Effect<T, any>
fx.watch(/*watcher*/ (data: T) => any)
-> Subscription
Аргументы
-
watcher
:(data: T) => any
Функция с сайд-эффектами, в качестве первого аргумента получает значение с которым был вызван эффект. Возвращаемое значение не используется
Возвращает
Subscription: Функция отмены подписки, после её вызова watcher
перестаёт получать обновления и удаляется из памяти. Повторные вызовы функции отмены подписки не делают ничего
Пример
import { createEffect } from "effector";
const fx = createEffect((params) => params);
fx.watch((params) => {
console.log("эффект вызван с аргументом", params);
});
await fx(10);
// => эффект вызван с аргументом 10
prepend(fn)
Создаёт событие-триггер для преобразования данных перед запуском эффекта. По сравнению с map, работает в обратном направлении
Формула
declare const fx: Effect<S, any>
const trigger = fx.prepend(/*fn*/(data: T) => S)
-> Event<T>
При вызове события trigger
, функция-обработчик fn
будет вызвана с поступившими данными, после чего fx
будет вызван с результатом вычислений
trigger -> fn -> fx
Аргументы
-
fn
:(data: T) => S
Функция-обработчик, которая будет вычислять данные для передачи в
fx
на основе данных событияtrigger
. Должна быть чистойАргументы
data
: Данные с которыми сработало событиеtrigger
Возвращает
Данные для передачи в
fx
Возвращает
Новое событие
map(fn)
Создает производное событие на основе данных эффекта
Формула
declare const fxA: Effect<T, any>
const eventB = fxA.map(/*fn*/(data: T) => S)
-> Event<S>
При вызове fxA
, функция-обработчик fn
будет вызвана с поступившими данными, после чего производное событие eventB
будет вызвано с результатом вычислений
fxA -> fn -> eventB
Аргументы
-
fn
:(data: T) => S
Функция-обработчик, которая будет вычислять данные для передачи в производное событие
eventB
на основе данных изfxA
. Должна быть чистойАргументы
data
: Данные с которыми сработал эффектfxA
Возвращает
Данные для передачи в производное событие
eventB
Возвращает
Новое, производное событие
Пример
import { createEffect } from "effector";
const updateUserFx = createEffect(({ name, role }) => {});
const userNameUpdate = updateUserFx.map(({ name }) => name);
const userRoleUpdate = updateUserFx.map(({ role }) => role.toUpperCase());
userNameUpdate.watch((name) => {
console.log(`Началось изменение имени пользователя на ${name}`);
});
userRoleUpdate.watch((role) => {
console.log(`Началось изменение роли пользователя на ${role}`);
});
await updateUserFx({ name: "john", role: "admin" });
// => Началось изменение имени пользователя на john
// => Началось изменение роли пользователя на ADMIN
Свойства
done
Событие, которое срабатывает с результатом выполнения эффекта и аргументом, переданным при вызове
Формула
declare const fx: Effect<P, D>
fx.done
-> Event<{params: P; result: D}>
Это свойство управляется самим эффектом
Пример
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
Событие, которое срабатывает с результатом выполнения эффекта
Формула
declare const fx: Effect<any, D>
fx.doneData
-> Event<D>
Это свойство управляется самим эффектом
Добавлено в effector 20.12.0
Пример
import { createEffect } from "effector";
const fx = createEffect((value) => value + 1);
fx.doneData.watch((result) => {
console.log(`Эффект успешно выполнился, вернув ${result}`);
});
await fx(2);
// => Эффект успешно выполнился, вернув 3
fail
Событие, которое срабатывает с ошибкой, возникшей при выполнении эффекта и аргументом, переданным при вызове
Формула
declare const fx: Effect<P, any, E>
fx.fail
-> Event<{params: P; error: E}>
Это свойство управляется самим эффектом
Пример
import { createEffect } from "effector";
const fx = createEffect(async (value) => {
throw Error(value - 1);
});
fx.fail.watch(({ params, error }) => {
console.log("Вызов с аргументом", params, "завершился с ошибкой", error.message);
});
fx(2);
// => Вызов с аргументом 2 завершился с ошибкой 1
failData
Добавлено в effector 20.12.0
Событие, которое срабатывает с ошибкой, возникшей при выполнении эффекта
Формула
declare const fx: Effect<any, any, E>
fx.failData
-> Event<E>
Это свойство управляется самим эффектом
Пример
import { createEffect } from "effector";
const fx = createEffect(async (value) => {
throw Error(value - 1);
});
fx.failData.watch((error) => {
console.log(`Вызов завершился с ошибкой ${error.message}`);
});
fx(2);
// => Вызов завершился с ошибкой 1
finally
Добавлено в effector 20.0.0
Событие, которое срабатывает при завершении эффекта с подробной информацией об аргументах, результатах и статусе выполнения
Формула
declare const fx: Effect<P, D, E>
fx.finally
-> Event<
| {status: 'done'; params: P; result: D}
| {status: 'fail'; params: P; error: E}
>
Это свойство управляется самим эффектом
Пример
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
Стор, который показывает, что эффект находится в процессе выполнения
Формула
declare const fx: Effect<any, any>
fx.pending
-> Store<boolean>
Это свойство избавляет от необходимости писать подобный код:
import { createEffect, createStore } from "effector";
const requestFx = createEffect();
const $isRequestPending = createStore(false)
.on(requestFx, () => true)
.on(requestFx.done, () => false)
.on(requestFx.fail, () => false);
Это свойство управляется самим эффектом
Пример
Отображение индикатора загрузки с React
import React from "react";
import ReactDOM from "react-dom";
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>;
};
ReactDOM.render(<App />, document.getElementById("root"));
fetchApiFx(1000);
// => true
// => false
inFlight
Добавлено в effector 20.11.0
Стор, который показывает число запущенных эффектов, которые находятся в процессе выполнения. Используется для ограничения числа одновременных запросов
Формула
declare const fx: Effect<any, any>
fx.inFlight
-> Store<number>
Это свойство избавляет от необходимости писать подобный код:
import { createEffect, createStore } from "effector";
const requestFx = createEffect();
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
shortName
Имя эффекта. Задаётся либо явно, через поле name
в createEffect, либо автоматически через babel plugin. Используется для обработки сущностей программно, например при использовании хуков домена
Формула
declare const fx: Effect<any, any>
fx.shortName
-> string
sid
Стабильный идентификатор эффекта. Задаётся автоматически через Babel plugin
Формула
declare const fx: Effect<any, any>
fx.sid
-> string | null
Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.
Помните, что переведенные статьи могут быть неактуальными, поэтому для получения наиболее точной и актуальной информации рекомендуем использовать оригинальную англоязычную версию документации.