attach API

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.

Properties of attached effects

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

  1. You call the effect returned by attach, passing your parameters.
  2. If source is specified, Effector takes the current value from this store.
  3. If mapParams is specified, this function is called with your parameters and (if present) data from source.
  4. The result of the mapParams function is passed to the original effect.
  5. 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 application
const sendAnalyticsFx = createEffect(async (event: { name: string; data: any }) => {
console.log("Analytics:", event.name);
});
// in auth module - local copy
const trackAuthFx = attach({
effect: sendAnalyticsFx,
name: "trackAuthFx",
});
// handle only events from auth module
trackAuthFx.done.watch(() => {
console.log("Auth event tracked");
});
// in cart module - another local copy
const trackCartFx = attach({
effect: sendAnalyticsFx,
name: "trackCartFx",
});
// handle only events from cart module
trackCartFx.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 tracked

Run example.

attach({ 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 effect argument can be either an effect or a regular handler.
    • The source argument is not reactive - store changes don’t automatically trigger effect execution.
    • source can be a store, an object of stores, or an array of stores.
    • The domain parameter can only be passed if effect is a handler.

  • Examples

Simple usage with one store and handler:

// effect for loading data
const loadDataFx = createEffect(async (id: number) => {
return fetch(`/api/data/${id}`).then((res) => res.json());
});
// store with current id
const $currentId = createStore(1);
// effect with store binding
const loadCurrentDataFx = attach({
source: $currentId,
effect: async (id: number) => {
const res = await fetch(`/api/data/${id}`);
return await res.json();
},
});
  • 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 mapParams fails with an error, the attached effect will immediately complete its execution with error, and the original effect won’t be called.
    • mapParams must return the same type that originalFx accepts 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`.
    },
    });
  • 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 }

Run example

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

Run example

  • 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 mapParams fails with an error, the attached effect will immediately complete its execution with error, and the original effect won’t be called.
    • The mapParams function must return the same type that the effect in the effect argument accepts as parameters. In case of error, you need to control error type compatibility with the effect yourself, TypeScript won’t help here.
    • The source argument is not reactive - store changes don’t automatically trigger effect execution.
    • source can be a store, an object of stores, or an array of stores.
  • 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.

Contributors