import { attach } from "effector";С версии effector 22.4.0 можно проверить, создан ли эффект через метод attach — is.attached.
Создает новые эффекты на основе других эффектов и сторов. Позволяет маппить параметры и обрабатывать ошибки.
Основные случаи использования: декларативный способ передачи значений из сторов в эффекты и предобработка аргументов. Наиболее полезный случай — attach({ source, async effect }).
Прикрепленные эффекты являются такими же полноценными объектами, как и обычные эффекты, созданные через createEffect. Вы должны размещать их в тех же файлах, что и обычные эффекты, а также можете использовать ту же стратегию именования.
Методы
attach({effect})
Создает эффект, который будет вызывать effect с переданными параметрами как есть. Это позволяет создавать отдельные эффекты с общим поведением.
Формула
const attachedFx = attach({ effect: originalFx });- Когда
attachedFxвызывается,originalFxтакже вызывается. - Когда
originalFxзавершается (успешно/с ошибкой),attachedFxзавершается с тем же состоянием.
Аргументы
effect(Effect): Обернутый эффект.
Возвращает
Effect: Новый эффект.
Типы
const originalFx: Effect<Params, Done, Fail>;
const attachedFx: Effect<Params, Done, Fail> = attach({ effect: originalFx,});В этом простом варианте attach типы originalFx и attachedFx будут одинаковыми.
Примеры
Это позволяет создать локальную копию эффекта, чтобы реагировать только на вызовы из текущего локального кода.
import { createEffect, attach } from "effector";
const originalFx = createEffect((word: string) => { console.info("Напечатано:", word);});
const attachedFx = attach({ effect: originalFx });
originalFx.watch(() => console.log("originalFx"));originalFx.done.watch(() => console.log("originalFx.done"));
attachedFx.watch(() => console.log("attachedFx"));attachedFx.done.watch(() => console.log("attachedFx.done"));
originalFx("первый");// => originalFx// => Напечатано: первый// => originalFx.done
attachedFx("второй");// => attachedFx// => originalFx// Напечатано: второй// => originalFx.done// => attachedFx.doneattach({source, effect})
Создает эффект, который будет вызывать указанный эффект с данными из source стора.
Формула
const attachedFx = attach({ source, effect: originalFx,});- Когда
attachedFxвызывается, данные изsourceчитаются, иoriginalFxвызывается с этими данными. - Когда
originalFxзавершается, то же состояние (успех/ошибка) передается вattachedFx, и он завершается.
Аргументы
source(Store |{[key: string]: Store}): Стор или объект с сторами, значения которых будут переданы во второй аргументmapParams.effect(Effect): Исходный эффект.
Возвращает
Effect: Новый эффект.
Типы
Вам не нужно явно указывать типы для каждого объявления. Следующий пример предназначен для лучшего понимания.
В большинстве случаев вы будете писать код так, без явных типов для let/const:
const originalFx = createEffect<OriginalParams, SomeResult, SomeError>(async () => {});const $store = createStore(initialValue);
const attachedFx = attach({ source: $store, effect: originalFx,});Один стор
const originalFx: Effect<T, Done, Fail>;const $store: Store<T>;
const attachedFx: Effect<void, Done, Fail> = attach({ source: $store, effect: originalFx,});Попробуйте в песочнице TypeScript
Типы стора в source и параметров effect должны совпадать.
Но attachedFx будет опускать тип параметров, что означает, что прикрепленный эффект не требует никаких параметров.
Объект стора
const originalFx: Effect<{ a: A; b: B }, Done, Fail>;const $a: Store<A>;const $b: Store<B>;
const attachedFx: Effect<void, Done, Fail> = attach({ source: { a: $a, b: $b }, effect: originalFx,});Попробуйте в песочнице TypeScript
Типы объекта source должны совпадать с параметрами originalFx. Но attachedFx будет опускать тип параметров, что означает, что прикрепленный эффект не требует никаких параметров.
Примеры
import { createEffect, createStore, attach } from "effector";
const requestPageFx = createEffect<{ page: number; size: number }, string[]>( async ({ page, size }) => { console.log("Запрошено", page); return page * size; },);
const $page = createStore(1);const $size = createStore(20);
const requestNextPageFx = attach({ source: { page: $page, size: $size }, effect: requestPageFx,});
$page.on(requestNextPageFx.done, (page) => page + 1);
requestPageFx.doneData.watch((position) => console.log("requestPageFx.doneData", position));
await requestNextPageFx();// => Запрошено 1// => requestPageFx.doneData 20
await requestNextPageFx();// => Запрошено 2// => requestPageFx.doneData 40
await requestNextPageFx();// => Запрошено 3// => requestPageFx.doneData 60attach({source, async effect})
Создает эффект, который будет вызывать асинхронную функцию с данными из source стора.
Формула
const attachedFx = attach({ source, async effect(source, params) {},});- Когда
attachedFxвызывается, данные изsourceчитаются, и вызывается функцияeffect. - Когда функция
effectвозвращает успешныйPromise,attachedFxзавершается с данными из функции какattachedFx.done. - Когда функция
effectвыбрасывает исключение или возвращает отклоненныйPromise,attachedFxзавершается с данными из функции какattachedFx.fail.
Аргументы
effect(Function):(source: Source, params: Params) => Promise<Result> | Resultsource(Store |{[key: string]: Store}): Стор или объект с сторами, значения которых будут переданы в первый аргументeffect.
Возвращает
Effect: Новый эффект.
Использование с областью видимости
Любые эффекты, вызванные внутри функции async effect, будут распространять область видимости.
const outerFx = createEffect((count: number) => { console.log("Попадание", count);});
const $store = createStore(0);const attachedFx = attach({ source: $store, async effect(count, _: void) {},});Область видимости теряется, если есть любые асинхронные вызовы функций:
const attachedFx = attach({ source: $store, async effect(source) { // Здесь всё в порядке, эффект вызывается const resultA = await anotherFx();
// Будьте осторожны: const resultB = await regularFunction(); // Здесь область видимости потеряна. },});Чтобы решить эту проблему, просто оберните вашу regularFunction в эффект:
const regularFunctionFx = createEffect(regularFunction);Типы
Один стор
const $store: Store<T>;
const attachedFx: Effect<Params, Done, Fail> = attach({ source: $store, async effect(source, params: Params): Done | Promise<Done> {},});Вам нужно явно указать только аргумент params. Все остальные типы аргументов должны быть выведены автоматически. Также вы можете явно указать тип возвращаемого значения функции effect.
Если вы хотите удалить любые аргументы из attachedFx, просто удалите второй аргумент из функции effect:
const attachedFx: Effect<void, void, Fail> = attach({ source: $store, async effect(source) {},});Несколько сторов
Для подробностей ознакомьтесь с предыдущим разделом типов. Здесь та же логика.
// Пример пользовательского кода без явных объявлений типовconst $foo = createStore(100);const $bar = createStore("demo");
const attachedFx = attach({ source: { foo: $foo, bar: $bar }, async effect({ foo, bar }, { baz }: { baz: boolean }) { console.log("Попадание!", { foo, bar, baz }); },});
attachedFx({ baz: true });// => Попадание! { foo: 100, bar: "demo", baz: true }Попробуйте в песочнице TypeScript
Пример
Пожалуйста, создайте pull request через ссылку “Edit this page”.
attach({effect, mapParams})
Создает эффект, который будет вызывать указанный эффект, преобразуя параметры с помощью функции mapParams.
Формула
const attachedFx = attach({ effect: originalFx, mapParams,});- Когда
attachedFxвызывается, параметры передаются в функциюmapParams, затем результат передается вoriginalFx. - Когда
originalFxзавершается,attachedFxзавершается с тем же состоянием (успех/ошибка). - Если
mapParamsвыбрасывает исключение,attachedFxзавершается с ошибкой какattachedFx.fail. НоoriginalFxне будет вызван.
Аргументы
effect(Effect): Обернутый эффект.mapParams((newParams) => effectParams): Функция, которая принимает новые параметры и преобразует их в параметры для обернутогоeffect. Работает аналогично event.prepend. Ошибки в функцииmapParamsприведут к завершению прикрепленного эффекта с ошибкой.
Возвращает
Effect: Новый эффект.
Типы
const originalFx: Effect<A, Done, Fail>;
const attachedFx: Effect<B, Done, Fail> = attach({ effect: originalFx, mapParams: (params: B): A {},});mapParams должна возвращать тот же тип, который принимает originalFx в качестве параметров.
Если attachedFx должен вызываться без аргументов, то params можно безопасно удалить из mapParams:
const attachedFx: Effect<void, Done, Fail> = attach({ effect: originalFx, mapParams: (): A {},});Попробуйте в песочнице TypeScript
Но если функция mapParams выбрасывает исключение, вам нужно самостоятельно проверять совместимость типов, так как TypeScript не поможет.
const attachedFx: Effect<void, Done, Fail> = attach({ effect: originalFx, mapParams: (): A { throw new AnyNonFailType(); // Это может быть несовместимо с типом `Fail`. },});Примеры
Преобразование аргументов
import { createEffect, attach } from "effector";
const originalFx = createEffect((a: { input: number }) => a);
const attachedFx = attach({ effect: originalFx, mapParams(a: number) { return { input: a * 100 }; },});
originalFx.watch((params) => console.log("originalFx started", params));
attachedFx(1);// => originalFx { input: 100 }Обработка исключений
import { createEffect, attach } from "effector";
const originalFx = createEffect((a: { a: number }) => a);
const attachedFx = attach({ effect: originalFx, mapParams(a: number) { throw new Error("custom error"); return { a }; },});
attachedFx.failData.watch((error) => console.log("attachedFx.failData", error));
attachedFx(1);// => attachedFx.failData// => Error: custom errorattach({source, mapParams, effect})
Создает эффект, который будет читать значения из source стора, передавать их с параметрами в функцию mapParams, а затем вызывать effect с результатом.
Формула
Этот вариант attach работает аналогично attach({effect, mapParams}). Поэтому некоторые вещи опущены в этом разделе.
const attachedFx = attach({ source, mapParams, effect: originalFx,});- Когда
attachedFxвызывается, параметры передаются в функциюmapParamsвместе с данными изsource, затем результат передается вoriginalFx. - Когда
originalFxзавершается,attachedFxзавершается с тем же состоянием (успех/ошибка). - Если
mapParamsвыбрасывает исключение,attachedFxзавершается с ошибкой какattachedFx.fail. НоoriginalFxне будет вызван.
Аргументы
source(Store |{[key: string]: Store}): Стор или объект с сторами, значения которых будут переданы во второй аргументmapParams.mapParams((newParams, values) => effectParams): Функция, которая принимает новые параметры и текущее значениеsourceи объединяет их в параметры для обернутогоeffect. Ошибки в функцииmapParamsприведут к завершению прикрепленного эффекта с ошибкой.effect(Effect): Обернутый эффект.
Возвращает
Effect: Новый эффект.
Типы
Пожалуйста, создайте pull request через ссылку “Edit this page”.
Примеры
С фабрикой
import { createEffect, createStore } from "effector";
export const backendRequestFx = createEffect(async ({ token, data, resource }) => { return fetch(`https://example.com/api${resource}`, { method: "POST", headers: { Authorization: `Bearer ${token}`, }, body: JSON.stringify(data), });});
export const $requestsSent = createStore(0);
$requestsSent.on(backendRequestFx, (total) => total + 1);import { attach, createStore } from "effector";
const $token = createStore("guest_token");
export const authorizedRequestFx = attach({ effect: backendRequestFx, source: $token, mapParams: ({ data, resource }, token) => ({ data, resource, token }),});
export function createRequest(resource) { return attach({ effect: authorizedRequestFx, mapParams: (data) => ({ data, resource }), });}import { createRequest } from "./authorized";import { $requestsSent } from "./request";
const getUserFx = createRequest("/user");const getPostsFx = createRequest("/posts");
$requestsSent.watch((total) => { console.log(`Аналитика клиента: отправлено ${total} запросов`);});
const user = await getUserFx({ name: "alice" });/*POST https://example.com/api/user{"name": "alice"}Authorization: Bearer guest_token*/
// => Аналитика клиента: отправлено 1 запросов
const posts = await getPostsFx({ user: user.id });/*POST https://example.com/api/posts{"user": 18329}Authorization: Bearer guest_token*/
// => Аналитика клиента: отправлено 2 запросовЧтобы фабрика работала корректно, добавьте путь к ./api/authorized в опцию factories для Babel плагина:
{ plugins: [ [ "effector/babel-plugin", { factories: ["src/path-to-your-entity/api/authorized"], }, ], ],}Параметры
attach() также принимает дополнительные параметры, которые можно использовать при необходимости.
name
attach({ name: string });Позволяет явно задать имя созданного прикрепленного эффекта:
import { attach } from "effector";
const attachedFx = attach({ name: "anotherUsefulName", source: $store, async effect(source, params: Type) { // ... },});
attachedFx.shortName; // "anotherUsefulName"Этот параметр доступен в любом варианте attach.
domain
attach({ domain: Domain });Позволяет создать эффект внутри указанного домена.
Примечание: это свойство может использоваться только с обычной функцией
effect.
import { createDomain, createStore, attach } from "effector";
const reportErrors = createDomain();const $counter = createStore(0);
const attachedFx = attach({ domain: reportErrors, source: $counter, async effect(counter) { // ... },});Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.
Помните, что переведенные статьи могут быть неактуальными, поэтому для получения наиболее точной и актуальной информации рекомендуем использовать оригинальную англоязычную версию документации.