attach API
import { attach } from "effector";The attach method allows creating new effects based on existing ones with the ability to access data from stores. This method enables reusing effect logic with different parameters and automatically passing data from stores.
An attached effect is a full-fledged effect that has its own .done, .fail, and other properties. They trigger only when the attached effect is called, not the original one.
const originalFx = createEffect(async (x: number) => x * 2);const attachedFx = attach({ effect: originalFx });
originalFx.done.watch(() => console.log("original done"));attachedFx.done.watch(() => console.log("attached done"));
await attachedFx(5);// original done// attached done
await originalFx(5);// original done// (attached done won't trigger)Algorithm
- You call the effect returned by
attach, passing your parameters. - If
sourceis specified, Effector takes the current value from this store. - If
mapParamsis specified, this function is called with your parameters and (if present) data fromsource. - The result of the
mapParamsfunction is passed to the originaleffect. - The result of executing the original effect is returned.
attach Configuration Forms
Form | Description |
|---|---|
attach({ effect }) | Creates a local copy of the effect with the same behavior. |
attach({ source, effect }) | Creates an effect that automatically passes data from source to the original effect when called. |
attach({ mapParams, effect }) | Creates an effect with input parameter transformation via mapParams function before passing to the original effect. |
attach({ source, mapParams, effect }) | Creates an effect that combines data from source with input parameters via mapParams and passes the result to the original effect. |
Configurations
attach({ effect })
Creates a new attached effect that will call effect with the passed parameters as is. This allows creating separate effects with shared behavior.
- Formula
const attachedFx = attach({ effect, // original effect whose behavior we're copying name? // name of the new effect});- Type
export function attach<FX extends Effect<any, any, any>>(config: { effect: FX; name?: string;}): Effect<EffectParams<FX>, EffectResult<FX>, EffectError<FX>>;- Examples
This allows creating a local copy of an effect to react only to calls from the current local code.
// common effect for the entire applicationconst sendAnalyticsFx = createEffect(async (event: { name: string; data: any }) => { console.log("Analytics:", event.name);});
// in auth module - local copyconst trackAuthFx = attach({ effect: sendAnalyticsFx, name: "trackAuthFx",});
// handle only events from auth moduletrackAuthFx.done.watch(() => { console.log("Auth event tracked");});
// in cart module - another local copyconst trackCartFx = attach({ effect: sendAnalyticsFx, name: "trackCartFx",});
// handle only events from cart moduletrackCartFx.done.watch(() => { console.log("Cart event tracked");});
trackAuthFx({ name: "login", data: {} });// Analytics: login// Auth event tracked
trackCartFx({ name: "add_to_cart", data: {} });// Analytics: add_to_cart// Cart event trackedattach({ source, effect })
Creates a new attached effect that when called will run the original effect or handler with data from source.
- Formula
const attachedFx = attach({ source, // data source passed to the effect effect, // original effect whose behavior we're copying name? // name of the new effect domain? // domain, in case effect is a handler});- Type
export function attach< States extends StoreShape, FX extends | Effect<GetShapeValue<States>, any, any> | ((state: GetShapeValue<States>, ...params: any[]) => any),>(config: { source: States; effect: FX; name?: string; domain?: Domain;}): Effect<void, EffectResult<FX>, EffectError<FX>>;-
Features
- The
effectargument can be either an effect or a regular handler. - The
sourceargument is not reactive - store changes don’t automatically trigger effect execution. sourcecan be a store, an object of stores, or an array of stores.- The
domainparameter can only be passed ifeffectis a handler.
- The
-
Examples
Simple usage with one store and handler:
// effect for loading dataconst loadDataFx = createEffect(async (id: number) => { return fetch(`/api/data/${id}`).then((res) => res.json());});
// store with current idconst $currentId = createStore(1);
// effect with store bindingconst loadCurrentDataFx = attach({ source: $currentId, effect: async (id: number) => { const res = await fetch(`/api/data/${id}`); return await res.json(); },});source argument as object:
import { createEffect, createStore, attach } from "effector";
const requestPageFx = createEffect<{ page: number; size: number }, number>( async ({ page, size }) => { console.log("Requested", 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();// => Requested 1// => requestPageFx.doneData 20Usage with array of stores:
const $lat = createStore(55.7558);const $lon = createStore(37.6173);
// weather fetching effectconst fetchWeatherFx = createEffect(([lat, lon]: [number, number]) => fetch(`/api/weather?lat=${lat}&lon=${lon}`).then((res) => res.json()),);
// combining array of storesconst loadWeatherFx = attach({ source: combine([$lat, $lon]), effect: fetchWeatherFx,});- Return value
Returns a new effect.
attach({ mapParams, effect })
Creates a new attached effect that when called will run the original effect, transforming parameters with the mapParams function.
- Formula
const attachedFx = attach({ effect, // original effect whose behavior we're copying mapParams, // function for data transformation name? // name of the new effect});- Type
export function attach<Params, FX extends Effect<any, any, any>>(config: { effect: FX; mapParams: (params: Params) => EffectParams<FX>; name?: string;}): Effect<Params, EffectResult<FX>, EffectError<FX>>;-
Features
- If
mapParamsfails with an error, the attached effect will immediately complete its execution with error, and the original effect won’t be called. mapParamsmust return the same type thatoriginalFxaccepts as parameters. In case of error, you need to control error type compatibility with the effect yourself, TypeScript won’t help here.
const attachedFx: Effect<void, Done, Fail> = attach({effect: originalFx,mapParams: (): A {throw new AnyNonFailType(); // this might be incompatible with type `Fail`.},}); - If
-
Examples
Using mapParams to transform arguments:
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 }As well as handling exceptions:
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 error- Return value
Returns a new effect.
attach({ source, mapParams, effect })
Creates a new attached effect that will read values from the source store, pass them with parameters to the mapParams function, and then call effect with the result.
- Formula
const attachedFx = attach({ source, // data source passed to the effect mapParams, // function for data transformation effect, // original effect whose behavior we're copying name? // name of the new effect});- Type
export function attach< States extends StoreShape, FX extends Effect<any, any, any>, FN extends (params: any, source: GetShapeValue<States>) => EffectParams<FX>,>(config: { source: States; effect: FX; mapParams: FN; name?: string;}): Effect<Parameters<FN>[0], EffectResult<FX>, EffectError<FX>>;-
Features
- If
mapParamsfails with an error, the attached effect will immediately complete its execution with error, and the original effect won’t be called. - The
mapParamsfunction must return the same type that the effect in theeffectargument accepts as parameters. In case of error, you need to control error type compatibility with the effect yourself, TypeScript won’t help here. - The
sourceargument is not reactive - store changes don’t automatically trigger effect execution. sourcecan be a store, an object of stores, or an array of stores.
- If
-
Examples
import { createStore, createEvent, createEffect, attach, sample } from "effector";
const $credentials = createStore({ username: "", password: "" });const $authToken = createStore("");
const apiFx = createEffect(async ({ url, data, token }) => { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Authorization: token ? `Bearer ${token}` : "", }, body: JSON.stringify(data), }); return response.json();});
const loginFx = attach({ source: { creds: $credentials, token: $authToken }, mapParams: (_, { creds, token }) => ({ url: "/api/login", data: creds, token, }), effect: apiFx,});- Return value
Returns a new effect.
Related API and Articles
- API
Effect API- Description of effects, their methods and propertiescreateEffect- Creating a new effectsample- Key operator for building connections between unitsStore API- Description of stores, their methods and properties
- Articles