;
}
```
* **Detailed description**
This property eliminates the need to write code like this:
```js
const $isRequestPending = createStore(false)
.on(requestFx, () => true)
.on(requestFx.done, () => false)
.on(requestFx.fail, () => false);
```
* **Examples**
```jsx
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 {loading ? "Loading..." : "Loading complete"}
;
};
fetchApiFx(1000);
// => true
// => false
```
Run example.
***
#### `.inFlight`
[Derived store][storeTypes] that shows the number of running effects that are currently executing. Can be used to limit the number of concurrent requests.
* **Type**
```ts
interface Effect {
inFlight: Store;
}
```
* **Detailed description**
This property eliminates the need to write code like this:
```js
const $requestsInFlight = createStore(0)
.on(requestFx, (n) => n + 1)
.on(requestFx.done, (n) => n - 1)
.on(requestFx.fail, (n) => n - 1);
```
* **Examples**
```js
import { createEffect } from "effector";
const fx = createEffect(async () => {
await new Promise((resolve) => setTimeout(resolve, 500));
});
fx.inFlight.watch((amount) => {
console.log("requests in flight:", amount);
});
// => requests in flight: 0
const req1 = fx();
// => requests in flight: 1
const req2 = fx();
// => requests in flight: 2
await Promise.all([req1, req2]);
// => requests in flight: 1
// => requests in flight: 0
```
Run example.
***
#### `.sid`
Unique unit identifier. It's important to note that SID doesn't change on each application run, it's statically written into your application bundle for absolute unit identification. Set automatically through Babel plugin.
* **Type**
```ts
interface Effect {
sid: string | null;
}
```
***
#### `.shortName`
String property containing the variable name in which the effect was declared. Effect name. Set either explicitly through the `name` field in createEffect, or automatically through babel plugin.
* **Type**
```ts
interface Effect {
shortName: string;
}
```
***
#### `.compositeName`
Composite effect name (including domain and short name) — useful for logging and tracing.
* **Type**
```ts
interface Effect {
compositeName: {
shortName: string;
fullName: string;
path: Array;
};
}
```
* **Examples**
```ts
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"
// ]
// }
```
### Related API and Articles
* **API**
* createEffect - Creating a new effect
* Event API - Description of events, their methods and properties
* Store API - Description of stores, their methods and properties
* sample - Key operator for building connections between units
* attach - Creates new effects based on other effects
* **Articles**
* Working with effects
* How to type effects and other units
* Guide to testing effects and other units
# Event
import Tabs from "@components/Tabs/Tabs.astro";
import TabItem from "@components/Tabs/TabItem.astro";
## Event API
```ts
import { type Event, type EventCallable, createEvent } from "effector";
const event = createEvent();
```
An event in Effector represents a user action, a step in the application process, a command to execute, an intention to change something, and much more.
An event acts as an entry point into the reactive data flow — a simple way to tell the app "something happened."
> TIP this is your canonical event:
>
> If you're not familiar with events and how to work with them, start here: What are events and how to use them.
### Event Types
It’s important to understand that there are two types of events:
1. **Events**, created using createEvent or .prepend. These events are of type EventCallable and can be triggered directly or used in the target argument of the sample method.
2. **Derived events**, created using .map, .filter, or .filterMap. These are of type Event and **cannot be triggered or passed into target** — Effector triggers them internally in the correct order. However, you can subscribe to them via sample or watch.
### Event Interface
Available methods and properties:
| Method/Property
| Description |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| prepend(fn) | Creates a new event, transforms the input using `fn`, and passes it to the original event. |
| map(fn) | Creates a new derived event triggered with the result of `fn` after the original event is fired. |
| filter({fn}) | Creates a new derived event that fires only if `fn` returns `true`. |
| filterMap(fn) | Creates a new derived event triggered with `fn` if it's not `undefined`. |
| watch(watcher) | Adds a listener called on every event trigger. |
| subscribe(observer) | Low-level method to integrate the event with the `Observable` pattern. |
| sid | Unique unit identifier. |
| shortName | The variable name in which the event is declared. |
| compositeName | Full composite name (domain + shortName) — useful for logging and tracing. |
### Event Methods
#### `.prepend(fn)`
> INFO info:
>
> This method exists **only** for events that are not derived (`EventCallable`)!
> That means it can only be used on events created with createEvent.
Creates a new `EventCallable`, which calls `fn` and passes the transformed data to the original event.
* **Formula**
```ts
const second = first.prepend(fn);
```
* **Type**
```ts
event.prepend(
fn: (_: Before) => Payload
): EventCallable
```
* **Examples**
```ts
import { createEvent } from "effector";
// original event
const userPropertyChanged = createEvent();
const changeName = userPropertyChanged.prepend((name) => ({
field: "name",
value: name,
}));
const changeRole = userPropertyChanged.prepend((role) => ({
field: "role",
value: role.toUpperCase(),
}));
userPropertyChanged.watch(({ field, value }) => {
console.log(`User property "${field}" changed to ${value}`);
});
changeName("john");
// => User property "name" changed to john
changeRole("admin");
// => User property "role" changed to ADMIN
changeName("alice");
// => User property "name" changed to alice
```
Open example
You can treat this method as a wrapper function. Suppose you need to frequently call a function with an inconvenient API:
```ts
import { sendAnalytics } from "./analytics";
export function reportClick(item: string) {
const argument = { type: "click", container: { items: [arg] } };
return sendAnalytics(argument);
}
```
That’s exactly what `.prepend()` does:
```ts
import { sendAnalytics } from "./analytics";
export const reportClick = sendAnalytics.prepend((item: string) => {
return { type: "click", container: { items: [arg] } };
});
reportClick("example");
// reportClick triggered "example"
// sendAnalytics triggered with { type: "click", container: { items: ["example"] } }
```
* **Detailed description**
Works like a reversed .map. In `.prepend`, data is transformed **before** the event is triggered. In .map, it’s transformed **after**.
If the original event belongs to a domain, the new event will inherit that domain.
* **Return value**
Returns a new event.
***
#### `.map(fn)`
> INFO Cleanliness is our everything!:
>
> The function `fn` **must be pure**.
Creates a new **derived event**, which is triggered after the original event, using the result of function `fn` as its argument.
* **Formula**
```ts
// Works for any event — both regular and derived
const first: Event | EventCallable;
const second: Event = first.map(fn);
```
* **Type**
```ts
event.map(fn: (payload: Payload) => T): Event
```
* **Examples**
```ts
import { createEvent } from "effector";
const userUpdated = createEvent<{ name: string; role: string }>();
// You can split data flow with .map()
const userNameUpdated = userUpdated.map(({ name }) => name);
// Or transform the data
const userRoleUpdated = userUpdated.map((user) => user.role.toUpperCase());
userNameUpdated.watch((name) => console.log(`User name is now [${name}]`));
userRoleUpdated.watch((role) => console.log(`User role is now [${role}]`));
userUpdated({ name: "john", role: "admin" });
// => User name is now [john]
// => User role is now [ADMIN]
```
Open example
* **Detailed description**
The `.map` method allows you to split and control the data flow, extract fields, or transform values within your business logic.
* **Return value**
Returns a new derived event.
***
#### `.filter({ fn })`
> TIP Tip:
>
> sample with the `filter` argument is the preferred method for filtering:
>
> ```ts
> const event = createEvent();
>
> const filteredEvent = sample({
> clock: event,
> filter: () => true,
> });
> ```
`.filter` creates a **derived** event, which is triggered **only** if the function `fn` returns `true`. This is helpful for branching the data flow and reacting to specific conditions.
* **Formula**
```ts
const first: Event | EventCallable;
const second: Event = first.filter({ fn });
```
* **Type**
```ts
event.filter(config: {
fn(payload: Payload): boolean
}): Event
```
* **Examples**
```js
import { createEvent, createStore } from "effector";
const numbers = createEvent();
const positiveNumbers = numbers.filter({
fn: ({ x }) => x > 0,
});
const $lastPositive = createStore(0);
$lastPositive.on(positiveNumbers, (n, { x }) => x);
$lastPositive.watch((x) => {
console.log("Last positive number:", x);
});
// => Last positive number: 0
numbers({ x: 0 }); // no output
numbers({ x: -10 }); // no output
numbers({ x: 10 }); // => Last positive number: 10
```
Open example
```js
import { createEvent, createStore, sample } from "effector";
const numbers = createEvent();
const positiveNumbers = sample({
clock: numbers,
filter: ({ x }) => x > 0,
});
const $lastPositive = createStore(0);
$lastPositive.on(positiveNumbers, (n, { x }) => x);
$lastPositive.watch((x) => {
console.log("Last positive number:", x);
});
// => Last positive number: 0
numbers({ x: 0 }); // no output
numbers({ x: -10 }); // no output
numbers({ x: 10 }); // => Last positive number: 10
```
* **Return value**
Returns a new derived event.
***
#### `.filterMap(fn)`
> TIP Our beloved sample:
>
> This method can also be replaced with a sample operation using the `filter` + `fn` arguments:
>
> ```ts
> const event = createEvent();
>
> const filteredAndMappedEvent = sample({
> clock: event,
> filter: () => true,
> fn: () => "value",
> });
> ```
This method creates a derived event, which **may** be triggered if the result of `fn` is **not undefined**. It combines filtering and mapping in a single step.
Ideal for working with JavaScript APIs that sometimes return `undefined`.
* **Formula**
```ts
const first: Event | EventCallable;
const second: Event = first.filterMap(fn);
```
* **Type**
```ts
event.filterMap(fn: (payload: Payload) => T | undefined): Event
```
* **Examples**
```tsx
import { createEvent } from "effector";
const listReceived = createEvent();
// Array.prototype.find() returns undefined when the element isn't found
const effectorFound = listReceived.filterMap((list) => {
return list.find((name) => name === "effector");
});
effectorFound.watch((name) => console.info("Found:", name));
listReceived(["redux", "effector", "mobx"]); // => Found: effector
listReceived(["redux", "mobx"]); // no output
```
> INFO Attention:
>
> The function `fn` must return some data. If `undefined` is returned, the derived event call will be skipped.
Open example
* **Return value**
Returns a new derived event.
***
#### `.watch(watcher)`
The `.watch` method calls the provided `watcher` callback **every time** the event is triggered.
> TIP Remember:
>
> The `watch` method does not handle or report exceptions, does not manage the completion of asynchronous operations, and does not resolve data race conditions.
>
> Its primary purpose is for short-term debugging and logging.
Learn more in the Events section.
* **Formula**
```ts
const event: Event | EventCallable;
const unwatch: () => void = event.watch(fn);
```
* **Type**
```ts
event.watch(watcher: (payload: Payload) => any): Subscription
```
* **Examples**
```js
import { createEvent } from "effector";
const sayHi = createEvent();
const unwatch = sayHi.watch((name) => console.log(`${name}, hello!`));
sayHi("Peter"); // => Peter, hello!
unwatch();
sayHi("Drew"); // => nothing happens
```
Open example
* **Return value**
Returns a function to cancel the subscription.
***
#### `.subscribe(observer)`
This is a **low-level** method for integrating events with the standard `Observable` pattern.
Further reading:
* [RxJS Observables](https://rxjs.dev/guide/observable)
* [TC39 proposal for Observables](https://github.com/tc39/proposal-observable)
> INFO Remember:
>
> You don't need to use this method yourself. It's used under the hood by rendering engines and so on.
* **Formula**
```ts
const event = createEvent();
event.subscribe(observer);
```
* **Type**
```ts
event.subscribe(observer: Observer): Subscription
```
* **Examples**
```ts
import { createEvent } from "effector";
const userLoggedIn = createEvent();
const subscription = userLoggedIn.subscribe({
next: (login) => {
console.log("User login:", login);
},
});
userLoggedIn("alice"); // => User login: alice
subscription.unsubscribe();
userLoggedIn("bob"); // => nothing happens
```
***
### Event Properties
These properties are mainly set using effector/babel-plugin or @effector/swc-plugin, so they are only available when using Babel or SWC.
#### `.sid`
A **unique identifier** for each event.
SID is **statically recorded** in your application bundle and doesn’t change between app runs. This makes it perfect for identifying units across threads or between client and server.
Example: [examples/worker-rpc](https://github.com/effector/effector/tree/master/examples/worker-rpc)
* **Type**
```ts
interface Event {
sid: string | null;
}
```
***
#### `.shortName`
Contains the **variable name** in which the event was declared.
```ts
import { createEvent } from "effector";
const demo = createEvent();
// demo.shortName === 'demo'
```
Reassigning the event to another variable doesn’t change this:
```ts
const another = demo;
// another.shortName === 'demo'
```
* **Type**
```ts
interface Event {
shortName: string;
}
```
***
#### `.compositeName`
Contains the **full path** of the event in your app’s structure. If the event was created inside a domain, its name will reflect that.
> TIP TIP:
>
> Usually, if a long name is required, it's better to pass it explicitly in the `name` field.
```ts
import { createEvent, createDomain } from "effector";
const first = createEvent();
const domain = createDomain();
const second = domain.createEvent();
console.log(first.compositeName);
// {
// shortName: "first",
// fullName: "first",
// path: ["first"]
// }
console.log(second.compositeName);
// {
// shortName: "second",
// fullName: "domain/second",
// path: ["domain", "second"]
// }
```
* **Type**
```ts
interface Event {
compositeName: {
shortName: string;
fullName: string;
path: Array;
};
}
```
### Event Peculiarities
1. In Effector, every event supports **only one argument**.
If you call an event like `someEvent(first, second)`, only the **first argument** will be used — the rest are ignored.
2. Inside event methods, **you must not call other events or effects**.
All provided functions must be **pure** — no side effects, no async calls.
### Related APIs and Articles
* **API**
* createEvent — create a new event
* createApi — create a set of events for a store
* merge — merge multiple events into one
* sample — core operator to connect units
* **Articles**
* How to work with events
* Thinking in Effector and why events matter
* TypeScript guide to events and units
# Scope API
## Scope API
```ts
import { type Scope, fork } from "effector";
const scope = fork();
```
`Scope` is a fully isolated instance of application.
The primary purpose of scope includes SSR (Server-Side Rendering) but is not limited to this use case. A `Scope` contains an independent clone of all units (including connections between them) and basic methods to access them.
> TIP scope matters:
>
> If you want to get deeper about scopes then check out great article about isolated scopes.
> We also have few related guides:
>
> * How to fix lost scope
> * Using scopes with SSR
> * Writing test for units
### Scope peculiarities
1. There are a few rules that must be followed to work successfully with scope.
2. Your scope can be lost to avoid this use .
### Scope methods
#### `.getState($store)`
Returns the value of a store in a given scope:
* **Formula**
```ts
const scope: Scope;
const $value: Store | StoreWritable;
const value: T = scope.getState($value);
```
* **Type**
```ts
scope.getState(store: Store): T;
```
* **Returns**
The value of the store.
* **Examples**
Create two instances of an application, trigger events in them, and test the `$counter` store value in both instances:
```js
import { createStore, createEvent, fork, allSettled } from "effector";
const inc = createEvent();
const dec = createEvent();
const $counter = createStore(0);
$counter.on(inc, (value) => value + 1);
$counter.on(dec, (value) => value - 1);
const scopeA = fork();
const scopeB = fork();
await allSettled(inc, { scope: scopeA });
await allSettled(dec, { scope: scopeB });
console.log($counter.getState()); // => 0
console.log(scopeA.getState($counter)); // => 1
console.log(scopeB.getState($counter)); // => -1
```
Try it.
### Related API and Articles
* **API**
* scopeBind – Method for binding a unit to a scope
* fork – Operator for creating a scope
* allSettled – Method for running a unit in a given scope and waiting for the entire chain of effects to complete
* serialize – Method for obtaining serialized store values
* hydrate – Method for hydrating serialized data
* **Articles**
* How to lose scope and fix it
* Using scopes with SSR
* How to test units
# Store API
## Store API
```ts
import { type Store, type StoreWritable, createStore } from "effector";
const $store = createStore();
```
A *Store* is an object that holds the state value. The store updates when the new value is not strictly equal (`!==`) to the current one and is not `undefined` (unless the store is configured with `skipVoid: false`). A store is a Unit. Some stores can be derived.
> TIP What is a store anyway?:
>
> If you're not yet familiar with how to work with a store, feel free to start here.
### Store Interface
Available store methods and properties:
| Method/Property | Description |
| ----------------------------------------------------- | ------------------------------------------------------------ |
| map(fn) | Creates a new derived store |
| on(trigger, reducer) | Updates state via a `reducer` when the `trigger` is fired |
| watch(watcher) | Calls the `watcher` function every time the store is updated |
| reset(...triggers) | Resets the store to its initial state |
| off(trigger) | Removes the subscription to the specified trigger |
| updates() | Event that fires when the store updates |
| reinit() | Event to reinitialize the store |
| shortName | ID or short name of the store |
| defaultState | Initial state of the store |
| getState() | Returns the current state |
### Immutability
A store in effector is immutable. This means that updates will only occur if the handler function (such as `combine`, `sample`, or `on`) returns a new object.
For example, before using array methods, you need to create a new reference to it. Here’s how to do it correctly:
```ts
$items.on(addItem, (items, newItem) => {
const updatedItems = [...items];
// ✅ .push method is called on a new array
updatedItems.push(newItem);
return updatedItems;
});
```
This approach should not be used, as the store **will not be updated**:
```ts
$items.on(addItem, (items, newItem) => {
// ❌ Error! The array reference remains the same, the store will not be updated
items.push(newItem);
return items;
});
```
Updating objects works in a similar way.
A store in effector should be as small as possible, responsible for a specific part of the business logic, unlike, for example, Redux, whose store tends to hold everything together. When the state is atomic, the need for spreading objects becomes less frequent. However, if there is a need to frequently update deeply nested data, it is acceptable to use [immer](https://immerjs.github.io/immer/produce) to simplify repetitive code when updating the state.
### Store Methods
#### `.map(fn)`
Accepts a function `fn` and returns a derived store that automatically updates when the original store changes.
> TIP Recommendation:
>
> For creating derived stores, prefer using combine($store, fn) over `.map()` for better composability and consistency with other combine forms.
* **Formulae**
```ts
$source.map(fn, config?);
```
* **Type**
```ts
const $derived = $source.map(
fn: (value: SourceValue) => T,
config?: {
skipVoid?: boolean
}
): Store
```
* **Examples**
Basic usage:
```ts
import { createEvent, createStore } from "effector";
const changed = createEvent();
const $title = createStore("");
const $titleLength = $title.map((title) => title.length);
$title.on(changed, (_, newTitle) => newTitle);
$titleLength.watch((length) => {
console.log("new length", length);
});
changed("hello");
changed("world");
changed("hello world");
```
Try it
You can pass a config object with `skipVoid: false` to allow the store to accept `undefined`:
```js
const $titleLength = $title.map((title) => title.length, { skipVoid: false });
```
* **Detailed Description**
The `map` method runs the function `fn` with the current store state as input every time the original store updates.
The return value becomes the new state of the derived store.
* **Returns**
Returns a new derived store.
#### `.on(trigger, reducer)`
Updates state using a reducer when the `trigger` is fired.
* **Formulae**
```ts
$store.on(trigger, reducer);
```
* **Type**
```ts
$store.on(
trigger: Unit | Unit[]
reducer: (state: State, payload: T) => State | void
): this
```
* **Examples**
```ts
import { createEvent, createStore } from "effector";
const $counter = createStore(0);
const incrementedBy = createEvent();
$counter.on(incrementedBy, (value, incrementor) => value + incrementor);
$counter.watch((value) => {
console.log("updated", value);
});
incrementedBy(2);
incrementedBy(2);
```
Try it
* **Returns**
Returns the current store.
#### `.watch(watcher)`
Calls the `watcher` function whenever the store updates.
* **Formulae**
```ts
const unwatch = $store.watch(watcher);
```
* **Type**
```ts
$store.watch(watcher: (state: State) => any): Subscription
```
* **Examples**
```ts
import { createEvent, createStore } from "effector";
const add = createEvent();
const $store = createStore(0);
$store.on(add, (state, payload) => state + payload);
$store.watch((value) => console.log(`current value: ${value}`));
add(4);
add(3);
```
Try it
* **Returns**
Returns a subscription cancellation function.
#### `.reset(...triggers)`
Resets the store to its default value when any of the `triggers` fire.
* **Formulae**
```ts
$store.reset(...triggers);
```
* **Type**
```ts
$store.reset(...triggers: Array>): this
```
* **Examples**
```ts
import { createEvent, createStore } from "effector";
const increment = createEvent();
const reset = createEvent();
const $store = createStore(0)
.on(increment, (state) => state + 1)
.reset(reset);
$store.watch((state) => console.log("changed", state));
increment();
increment();
reset();
```
Try it
* **Returns**
Returns the current store.
#### `.off(trigger)`
Removes the reducer for the specified `trigger`.
* **Formulae**
```ts
$store.off(trigger);
```
* **Type**
```ts
$store.off(trigger: Unit): this
```
* **Examples**
```ts
import { createEvent, createStore, merge } from "effector";
const changedA = createEvent();
const changedB = createEvent();
const $store = createStore(0);
const changed = merge([changedA, changedB]);
$store.on(changed, (state, params) => state + params);
$store.off(changed);
```
Try it
* **Returns**
Returns the current store.
### Store Properties
#### `.updates`
An event that fires on every store update.
* **Examples**
```ts
import { createStore, is } from "effector";
const $clicksAmount = createStore(0);
is.event($clicksAmount.updates); // true
$clicksAmount.updates.watch((amount) => {
console.log(amount);
});
```
Try it
* **Returns**
A derived event representing the store's updates.
#### `.reinit`
Event to reinitialize the store to its default state.
* **Examples**
```ts
import { createStore, createEvent, sample, is } from "effector";
const $counter = createStore(0);
is.event($counter.reinit);
const increment = createEvent();
$counter.reinit();
console.log($counter.getState());
```
Try it
* **Returns**
An event that reinitializes the store.
#### `.shortName`
A string property containing the store's ID or short name.
* **Examples**
```ts
const $store = createStore(0, {
name: "someName",
});
console.log($store.shortName); // someName
```
Try it
* **Returns**
The store’s ID or short name.
#### `.defaultState`
The store’s default state value.
* **Example**
```ts
const $store = createStore("DEFAULT");
console.log($store.defaultState === "DEFAULT"); // true
```
* **Returns**
The default state value.
### Utility Methods
#### `.getState()`
Returns the current state of the store.
> WARNING Caution!:
>
> Using `getState()` in business logic is not recommended — it's better to pass data through `sample`.
* **Examples**
```ts
import { createEvent, createStore } from "effector";
const add = createEvent();
const $number = createStore(0).on(add, (state, data) => state + data);
add(2);
add(3);
console.log($number.getState());
```
Try it
* **Returns**
The current state of the store.
### Related APIs
* createStore – Creates a new store
* combine – Combines multiple stores into a derived store
* sample – A core operator for connecting units
* createEvent – Creates an event
* createEffect – Creates an effect
# allSettled
## Methods
### `allSettled(unit, {scope, params?})`
Calls the provided unit within the current scope and wait for all triggered effects to complete.
#### Formulae
```ts
allSettled(unit: Event, {scope: Scope, params?: T}): Promise
allSettled(unit: Effect, {scope: Scope, params?: T}): Promise<
| {status: 'done'; value: Done}
| {status: 'fail'; value: Fail}
>
allSettled(unit: Store, {scope: Scope, params?: T}): Promise
```
#### Arguments
1. `unit`: or to be called
2. `scope`:
3. `params`: params passed to `unit`
> INFO since:
>
> Return value for effect is supported since [effector 21.4.0](https://changelog.effector.dev/#effector-21-4-0)
#### Examples
> TIP Contribution:
>
> TBD
>
> Please, [open PullRequest](https://github.com/effector/effector) and contribute examples for this section via "Edit this page" link below.
### `allSettled(scope)`
Checks the provided scope for any ongoing computations and wait for their completion.
#### Formulae
```ts
allSettled(scope): Promise
```
#### Arguments
1. `scope`:
> INFO since:
>
> Supported since effector 22.5.0
#### Examples
##### Usage in tests
For example, tests that validate the integration with an external reactive API
```ts
import {createEvent, sample, fork, scopeBind, allSettled} from 'effector'
test('integration with externalSource', async () => {
const scope = fork()
const updated = createEvent()
sample({
clock: updated,
target: someOtherLogicStart,
})
// 1. Subscribe event to external source
const externalUpdated = scopeBind(updated, {scope})
externalSource.listen(() => externalUpdates())
// 2. Trigger update of external source
externalSource.trigger()
// 3. Wait for all triggered computations in effector's scope, even though these were not triggered by effector itself
await allSettled(scope)
// 4. Check anything as usual
expect(...).toBe(...)
})
```
# attach API
import Tabs from "@components/Tabs/Tabs.astro";
import TabItem from "@components/Tabs/TabItem.astro";
[effectApi]: /en/api/effector/Effect
[storeApi]: /en/api/effector/Store
## `attach` API
```ts
import { attach } from "effector";
```
The `attach` method allows creating new [effects][effectApi] based on existing ones with the ability to access data from [stores][storeApi]. This method enables reusing effect logic with different parameters and automatically passing data from stores.
> INFO 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.
>
> ```ts
> 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][effectApi] that will call `effect` with the passed parameters as is. This allows creating separate effects with shared behavior.
* **Formula**
```ts
const attachedFx = attach({
effect, // original effect whose behavior we're copying
name? // name of the new effect
});
```
* **Type**
```ts
export function attach>(config: {
effect: FX;
name?: string;
}): Effect, EffectResult, EffectError>;
```
* **Examples**
This allows creating a *local* copy of an effect to react only to calls from the current *local* code.
```ts
// 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][effectApi] that when called will run the original `effect` or handler with data from `source`.
* **Formula**
```ts
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**
```ts
export function attach<
States extends StoreShape,
FX extends
| Effect, any, any>
| ((state: GetShapeValue, ...params: any[]) => any),
>(config: {
source: States;
effect: FX;
name?: string;
domain?: Domain;
}): Effect, EffectError>;
```
* **Features**
* The `effect` argument can be either an [effect][effectApi] 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:
```ts
// 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();
},
});
```
`source` argument as object:
```ts
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 20
```
Usage with array of stores:
```ts
const $lat = createStore(55.7558);
const $lon = createStore(37.6173);
// weather fetching effect
const fetchWeatherFx = createEffect(([lat, lon]: [number, number]) =>
fetch(`/api/weather?lat=${lat}&lon=${lon}`).then((res) => res.json()),
);
// combining array of stores
const loadWeatherFx = attach({
source: combine([$lat, $lon]),
effect: fetchWeatherFx,
});
```
* **Return value**
Returns a new [effect][effectApi].
#### `attach({ mapParams, effect })`
Creates a new attached [effect][effectApi] that when called will run the original `effect`, transforming parameters with the `mapParams` function.
* **Formula**
```ts
const attachedFx = attach({
effect, // original effect whose behavior we're copying
mapParams, // function for data transformation
name? // name of the new effect
});
```
* **Type**
```ts
export function attach>(config: {
effect: FX;
mapParams: (params: Params) => EffectParams;
name?: string;
}): Effect, EffectError>;
```
* **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.
```ts
const attachedFx: Effect = attach({
effect: originalFx,
mapParams: (): A {
throw new AnyNonFailType(); // this might be incompatible with type `Fail`.
},
});
```
* **Examples**
Using `mapParams` to transform arguments:
```ts
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:
```ts
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][effectApi].
#### `attach({ source, mapParams, effect })`
Creates a new attached [effect][effectApi] that will read values from the `source` store, pass them with parameters to the `mapParams` function, and then call `effect` with the result.
* **Formula**
```ts
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**
```ts
export function attach<
States extends StoreShape,
FX extends Effect,
FN extends (params: any, source: GetShapeValue) => EffectParams,
>(config: {
source: States;
effect: FX;
mapParams: FN;
name?: string;
}): Effect[0], EffectResult, EffectError>;
```
* **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**
```ts
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][effectApi].
### Related API and Articles
* **API**
* Effect API - Description of effects, their methods and properties
* createEffect - Creating a new effect
* sample - Key operator for building connections between units
* Store API - Description of stores, their methods and properties
* **Articles**
* Working with effects
* Typing
# Babel plugin
Built-in plugin for babel can be used for ssr and debugging. It inserts a name a Unit,
inferred from variable name and `sid` (Stable IDentifier), computed from the location in the source code.
For example, in case effects without handlers, it improves error messages by
clearly showing in which effect error happened.
```js
import { createEffect } from "effector";
const fetchFx = createEffect();
fetchFx();
// => no handler used in fetchFx
```
Try it
## Usage
In the simplest case, it can be used without any configuration:
```json
// .babelrc
{
"plugins": ["effector/babel-plugin"]
}
```
## SID
> INFO since:
>
> [effector 20.2.0](https://changelog.effector.dev/#effector-20-2-0)
Stable hash identifier for events, effects, stores and domains, preserved between environments, to handle client-server
interaction within the same codebase.
The crucial value of sid is that it can be autogenerated by `effector/babel-plugin` with default config, and it will be stable between builds.
> TIP Deep dive explanation:
>
> If you need the detailed deep-dive explanation about why we need SIDs and how they are used internally, you can find it by following this link
See [example project](https://github.com/effector/effector/tree/master/examples/worker-rpc)
```js
// common.js
import { createEffect } from "effector";
export const getUser = createEffect({ sid: "GET /user" });
console.log(getUsers.sid);
// => GET /user
```
```js
// worker.js
import { getUsers } from "./common.js";
getUsers.use((userID) => fetch(userID));
getUsers.done.watch(({ result }) => {
postMessage({ sid: getUsers.sid, result });
});
onmessage = async ({ data }) => {
if (data.sid !== getUsers.sid) return;
getUsers(data.userID);
};
```
```js
// client.js
import { createEvent } from "effector";
import { getUsers } from "./common.js";
const onMessage = createEvent();
const worker = new Worker("worker.js");
worker.onmessage = onMessage;
getUsers.use(
(userID) =>
new Promise((rs) => {
worker.postMessage({ sid: getUsers.sid, userID });
const unwatch = onMessage.watch(({ data }) => {
if (data.sid !== getUsers.sid) return;
unwatch();
rs(data.result);
});
}),
);
```
## Configuration
### `hmr`
> INFO since:
>
> [effector 23.4.0](https://changelog.effector.dev/#effector-23.4.0)
Enable Hot Module Replacement (HMR) support to clean up links, subscriptions and side effects managed by Effector. This prevents double-firing of Effects and watchers.
> WARNING Interaction with factories:
>
> HMR support show best results when all factories in project are properly declared, which help plugin and runtime to know which code to clear on hot updates
#### Formulae
```json
"effector/babel-plugin",
{
"hmr": "es"
}
]
```
* Type: `boolean` | `"es"` | `"cjs"`
* `true`: Use hmr with auto-detection of target case. Based on [supportsStaticESM](https://babeljs.io/docs/options#caller) babel feature with wide support in bundlers
* `"es"`: Use `import.meta.hot` HMR API in bundlers that are ESM-compliant, like Vite and Rollup
* `"cjs"`: Use `module.hot` HMR API in bundlers that rely on CommonJS modules, like Webpack, Next.js or React Native
* `false`: Disable Hot Module Replacement
* Default: `false`
> INFO In Production:
>
> When bundling for production, make sure to set the `hmr` option to `false` or remove it to reduce bundle size and improve runtime performance.
### `forceScope`
> INFO since:
>
> [effector 23.4.0](https://changelog.effector.dev/#effector-23.4.0)
Adds `forceScope` to all hooks from `effector-react`. This prevents mistakes when events called in non-scoped environment.
#### Formulae
```json
"effector/babel-plugin",
{
"forceScope": true
}
```
* Type: `boolean`
* `true`: Adds `{ forceScope: true }` to hooks like `useUnit`
* `false`: Do nothing
* Default: `false`
### `importName`
Specifying import name or names to process by plugin. Import should be used in the code as specified.
#### Formulae
```json
[
"effector/babel-plugin",
{
"importName": ["effector"]
}
]
```
* Type: `string | string[]`
* Default: `['effector', 'effector/compat']`
### `factories`
Accepts an array of module names which exports treat as custom factories, therefore, each function call provides a unique prefix for sids of units inside them. Used to
SSR(Server Side Rendering) and it's not required for client-only application.
> INFO since:
>
> [effector 21.6.0](https://changelog.effector.dev/#effector-21-6-0)
#### Formulae
```json
[
"effector/babel-plugin",
{
"factories": ["path/here"]
}
]
```
* Type: `string[]`
* Factories can have any number of arguments.
* Factories can create any number of units.
* Factories can call any effector methods.
* Factories can call other factories from other modules.
* Modules with factories can export any number of functions.
* Factories should be compiled with `effector/babel-plugin` as well as code which use them.
#### Examples
```json
// .babelrc
{
"plugins": [
[
"effector/babel-plugin",
{
"factories": ["src/createEffectStatus", "~/createCommonPending"]
}
]
]
}
```
```js
// ./src/createEffectStatus.js
import { rootDomain } from "./rootDomain";
export function createEffectStatus(fx) {
const $status = rootDomain.createStore("init").on(fx.finally, (_, { status }) => status);
return $status;
}
```
```js
// ./src/statuses.js
import { createEffectStatus } from "./createEffectStatus";
import { fetchUserFx, fetchFriendsFx } from "./api";
export const $fetchUserStatus = createEffectStatus(fetchUserFx);
export const $fetchFriendsStatus = createEffectStatus(fetchFriendsFx);
```
Import `createEffectStatus` from `'./createEffectStatus'` was treated as factory function, so each store created by it
has its own sid and will be handled by serialize
independently, although without `factories` they will share the same `sid`.
### `reactSsr`
Replaces imports from `effector-react` to `effector-react/scope`. Useful for building both server-side and client-side
builds from the same codebase.
> WARNING Deprecated:
>
> Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) the core team recommends deleting this option from `babel-plugin` configuration because effector-react supports SSR by default.
#### Formulae
```json
[
"effector/babel-plugin",
{
"reactSsr": false
}
]
```
* Type: `boolean`
* Default: `false`
### `addNames`
Adds name to units factories call. Useful for minification and obfuscation of production builds.
> INFO since:
>
> [effector 21.8.0](https://changelog.effector.dev/#effector-21-8-0)
#### Formulae
```json
[
"effector/babel-plugin",
{
"addNames": true
}
]
```
* Type: `boolean`
* Default: `true`
### `addLoc`
Adds location to methods' calls. Used by devtools, for example [effector-logger](https://github.com/effector/logger).
#### Formulae
```json
[
"effector/babel-plugin",
{
"addLoc": false
}
]
```
* Type: `boolean`
* Default: `false`
### `debugSids`
Adds a file path and variable name of a unit definition to a sid. Useful for debugging SSR.
#### Formulae
```json
[
"effector/babel-plugin",
{
"debugSids": false
}
]
```
* Type: `boolean`
* Default: `false`
### `transformLegacyDomainMethods`
Allows disabling transforming Unit creators on Domain. This option is useful when these transforms interfere with other libraries or your code.
The `effector/babel-plugin` may misidentify calls to unit creators because it is hard to know which variables are indeed Domains. If your project can't run due to this, you can turn these transforms off with this flag and pass `Domain` as an argument to regular unit creators, which is a better and more stable alternative.
> WARNING:
>
> Disabling this option will prevent units created with `Domain` methods from having a `sid` and other information. If your code relies on these methods, this will cause issues with your existing code.
#### Formulae
```json
[
"effector/babel-plugin",
{
"transformLegacyDomainMethods": false
}
]
```
* Type: `boolean`
* Default: `true`
### `noDefaults`
Option for `effector/babel-plugin` for making custom unit factories with clean configuration.
> INFO since:
>
> [effector 20.2.0](https://changelog.effector.dev/#effector-20-2-0)
#### Formulae
```json
[
"effector/babel-plugin",
{
"noDefaults": false
}
]
```
* Type: `boolean`
* Default: `false`
#### Examples
```json
// .babelrc
{
"plugins": [
["effector/babel-plugin", { "addLoc": true }],
[
"effector/babel-plugin",
{
"importName": "@lib/createInputField",
"storeCreators": ["createInputField"],
"noDefaults": true
},
"createInputField"
]
]
}
```
```js
// @lib/createInputField.js
import { createStore } from "effector";
import { resetForm } from "./form";
export function createInputField(defaultState, { sid, name }) {
return createStore(defaultState, { sid, name }).reset(resetForm);
}
```
```js
// src/state.js
import { createInputField } from "@lib/createInputField";
const foo = createInputField("-");
/*
will be treated as store creator and compiled to
const foo = createInputField('-', {
name: 'foo',
sid: 'z&si65'
})
*/
```
## Usage with Bundlers
### Vite + React (SSR)
To use with `effector/babel-plugin`, you have to following next steps:
1. Install `@vitejs/plugin-react` package.
2. `vite.config.js` should be follows:
> Note: `effector/babel-plugin` is not a package, it is bundled with `effector`
```js
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [
react({
babel: {
plugins: ["effector/babel-plugin"],
// Use .babelrc files
babelrc: true,
// Use babel.config.js files
configFile: true,
},
}),
],
});
```
# clearNode
```ts
import { clearNode } from "effector";
```
Method for destroying stores, events, effects, subscriptions, and domains.
## Methods
### `clearNode(unit, config?)`
#### Formulae
```ts
clearNode(unit, config?: {deep?: boolean}): void
```
#### Arguments
1. `unit` (////): unit to be erased.
2. `config: {}` (optional): config object.
* `deep?: boolean` (optional): erase node *and* all of its computed values.
#### Returns
`void`
#### Examples
##### Simple
```js
import { createStore, createEvent, clearNode } from "effector";
const inc = createEvent();
const $store = createStore(0).on(inc, (x) => x + 1);
inc.watch(() => console.log("inc called"));
$store.watch((x) => console.log("store state: ", x));
// => store state: 0
inc();
// => inc called
// => store state: 1
clearNode($store);
inc();
// => inc called
```
Try it
##### Deep clear
```js
import { createStore, createEvent, clearNode } from "effector";
const inc = createEvent();
const trigger = inc.prepend(() => {});
const $store = createStore(0).on(inc, (x) => x + 1);
trigger.watch(() => console.log("trigger called"));
inc.watch(() => console.log("inc called"));
$store.watch((x) => console.log("store state: ", x));
// => store state: 0
trigger();
// => trigger called
// => inc called
// => store state: 1
clearNode(trigger, { deep: true });
trigger();
// no reaction
inc();
// no reaction!
// all units, which depend on trigger, are erased
// including inc and store, because it depends on inc
```
Try it
# combine API
[storeApi]: /en/api/effector/Store
## `combine` API
```ts
import { combine } from "effector";
```
The `combine` method allows retrieving the state from each passed [store][storeApi] and combining them into a single value, storing it in a new derived store. The resulting store will update every time any of the passed stores is updated.
### Algorithm
1. `combine` reads the current state from all passed stores.
2. If a transformation function `fn` is provided, it calls `fn` with the values from stores.
3. The result is saved to a new derived store.
4. The derived store updates whenever any of the source stores changes.
### Peculiarities
* **Batching updates**: When multiple stores update simultaneously (during one tick), `combine` processes them all at once, resulting in a single update of the derived store.
* **Mixing stores with primitives**: You can pass not only stores but also primitives and objects to `combine`. Effector will not track mutations of these primitives and objects - they are treated as static values.
* **Pure transformation functions**: All transformation functions passed to `combine` must be pure.
* **Strict equality check**: If a transformation function returns the same value as previous (by `!==` comparison), the derived store won't update.
* **Error handling**: If a transformation function throws an error during execution, the application will crash. This will be fixed in [effector v24](https://github.com/effector/effector/issues/1163).
### Configuration forms
| Form
| Description |
| ----------------------------------------------------------- | ----------------------------------------------------------------------- |
| combine($a, fn) | Transforms a single store's value via `fn`. |
| combine(...$stores, fn?) | Combines multiple stores/values, optionally transforming via `fn`. |
| combine({ a: $a, b: $b }, fn?) | Combines stores into an object store, optionally transforming via `fn`. |
| combine(\[$a, $b], fn?) | Combines stores into an array store, optionally transforming via `fn`. |
### Configurations
#### `combine($store, fn)`
Creates a new derived store by transforming a single store's value. This is the **preferred method** for creating derived stores from a single source. It is an alternative way to Store.map().
* **Formula**
```ts
const $result = combine($source, (value) => {
// transformation logic
return result;
});
```
* **Type**
```ts
export function combine(
a: Store,
fn: (a: A) => R,
config?: {
skipVoid?: boolean;
},
): Store;
```
* **Features**
* The transformation function `fn` must be pure.
* If `fn` returns the same value as previous (by `!==` comparison), the store won't update.
* If `fn` throws an error during execution, the application will crash (this will be fixed in [effector v24](https://github.com/effector/effector/issues/1163)).
* Optional `config` parameter: see createStore for details on `skipVoid`.
* **Examples**
```ts
import { createStore, combine } from "effector";
const $name = createStore("John");
const $greeting = combine($name, (name) => `Hello, ${name}!`);
$greeting.watch((greeting) => console.log(greeting));
// => Hello, John!
```
```ts
import { createStore, combine } from "effector";
const $price = createStore(100);
const $priceWithTax = combine($price, (price) => price * 1.2);
$priceWithTax.watch((price) => console.log("Price with tax:", price));
// => Price with tax: 120
```
* **Return value**
Returns a new derived store.
***
#### `combine(...$stores, fn?)`
Creates a new derived store that combines any number of stores and values. Accepts any number of arguments - stores, primitives, or objects. The last argument can optionally be a transformation function.
Without `fn`, wraps all values into an array. With `fn`, transforms values using the function where stores are passed as separate arguments.
* **Formula**
```ts
// without transformation function
const $result = combine($a);
const $result = combine($a, $b, $c);
// with transformation function
const $result = combine($a, (value) => {
// transformation logic
return result;
});
const $result = combine($a, $b, $c, (a, b, c) => {
// transformation logic
return result;
});
```
* **Type**
```ts
export function combine(
...stores: T
): Store<{ [K in keyof T]: T[K] extends Store ? U : T[K] }>;
export function combine(
...stores: T,
fn: (...stores: { [K in keyof T]: T[K] extends Store ? U : T[K] }) => R,
config?: { skipVoid?: boolean },
): Store;
// also supports any number of stores with optional fn as last argument
```
* **Features**
* Accepts any number of stores, primitives, or objects as arguments.
* Can mix stores with values such as primitives or objects.
* Without `fn`, returns an array store with values in the same order as arguments.
* When `fn` is provided:
* The transformation function `fn` must be pure.
* If `fn` returns the same value as previous (by `!==` comparison), the store won't update.
* If `fn` throws an error during execution, the application will crash (this will be fixed in [effector v24](https://github.com/effector/effector/issues/1163)).
* Optional `config` parameter: see createStore for details on `skipVoid`.
* **Examples**
Without transformation function - creates an array store:
```ts
import { createStore, combine } from "effector";
const $firstName = createStore("John");
const $lastName = createStore("Doe");
const $age = createStore(30);
const $userData = combine($firstName, $lastName, $age);
$userData.watch((data) => console.log(data));
// => ["John", "Doe", 30]
```
With transformation function - transforms combined values:
```ts
import { createStore, combine } from "effector";
const $firstName = createStore("John");
const $lastName = createStore("Doe");
const $fullName = combine($firstName, $lastName, (first, last) => {
return `${first} ${last}`;
});
$fullName.watch((name) => console.log(name));
// => "John Doe"
```
Combining multiple stores with transformation:
```ts
import { createStore, combine } from "effector";
const $price = createStore(100);
const $quantity = createStore(2);
const $discount = createStore(0.1);
const $total = combine($price, $quantity, $discount, (price, qty, disc) => {
const subtotal = price * qty;
return subtotal - subtotal * disc;
});
$total.watch((total) => console.log(`Total: $${total}`));
// => Total: $180
```
Mixing stores with primitives:
```ts
import { createStore, combine } from "effector";
const $userName = createStore("Alice");
const API_URL = "https://api.example.com";
const $userEndpoint = combine($userName, API_URL, (name, url) => {
return `${url}/users/${name}`;
});
$userEndpoint.watch((endpoint) => console.log(endpoint));
// => https://api.example.com/users/Alice
```
* **Return value**
Returns a new derived store.
***
#### `combine({ a: $a, b: $b }, fn?)`
Creates a new derived store that combines an object of stores. Without `fn`, it creates an object store with values from the passed stores. With `fn`, it transforms the combined values using the function.
* **Formula**
```ts
// without transformation function
const $result = combine({
a: $a,
b: $b,
// ... more stores
});
// with transformation function
const $result = combine({ a: $a, b: $b, c: $c }, ({ a, b, c }) => {
// transformation logic
return result;
});
```
* **Type**
```ts
export function combine(
shape: State,
fn?: (shape: { [K in keyof State]: State[K] extends Store ? U : State[K] }) => R,
config?: {
skipVoid?: boolean;
},
): Store;
```
* **Features**
* Updates are batched when multiple stores change simultaneously.
* Can mix stores with values such as primitives or objects.
* if `fn` is provided:
* The transformation function `fn` must be pure.
* If `fn` returns the same value as previous (by `!==` comparison), the store won't update.
* If `fn` throws an error during execution, the application will crash (this will be fixed in [effector v24](https://github.com/effector/effector/issues/1163)).
* Optional `config` parameter: see createStore for details on `skipVoid`.
* **Examples**
Without transformation function - creates an object store:
```ts
import { createStore, combine } from "effector";
const $firstName = createStore("John");
const $lastName = createStore("Doe");
const $age = createStore(30);
const $user = combine({
firstName: $firstName,
lastName: $lastName,
age: $age,
});
$user.watch((user) => console.log(user));
// => { firstName: "John", lastName: "Doe", age: 30 }
```
With transformation function - transforms object values:
```ts
import { createStore, combine } from "effector";
const $firstName = createStore("John");
const $lastName = createStore("Doe");
const $age = createStore(30);
const $userSummary = combine(
{ firstName: $firstName, lastName: $lastName, age: $age },
({ firstName, lastName, age }) => {
return `${firstName} ${lastName}, ${age} years old`;
},
);
$userSummary.watch((summary) => console.log(summary));
// => "John Doe, 30 years old"
```
Practical example - form validation:
```ts
import { createStore, combine } from "effector";
const $email = createStore("");
const $password = createStore("");
const $confirmPassword = createStore("");
const $formValidation = combine(
{ email: $email, password: $password, confirmPassword: $confirmPassword },
({ email, password, confirmPassword }) => {
const errors = [];
if (!email.includes("@")) {
errors.push("Invalid email");
}
if (password.length < 8) {
errors.push("Password must be at least 8 characters");
}
if (password !== confirmPassword) {
errors.push("Passwords don't match");
}
return {
isValid: errors.length === 0,
errors,
};
},
);
```
Mixing stores with primitives and objects:
```ts
import { createStore, combine } from "effector";
const $userId = createStore(123);
const $isActive = createStore(true);
const $userData = combine({
id: $userId,
isActive: $isActive,
role: "user",
permissions: ["read", "write"],
});
$userData.watch((data) => console.log(data));
// => { id: 123, isActive: true, role: "user", permissions: ["read", "write"] }
```
* **Return value**
Returns a new derived store.
***
#### `combine([$a, $b], fn?)`
Creates a new derived store that combines an array of stores. Without `fn`, it creates an array store with values from the passed stores in the same order. With `fn`, it transforms the combined values using the function.
* **Formula**
```ts
// without transformation function
const $result = combine([$a, $b, $c]);
// with transformation function
const $result = combine([$a, $b, $c], ([a, b, c]) => {
// transformation logic
return result;
});
```
* **Type**
```ts
export function combine(
tuple: State,
): Store<{ [K in keyof State]: State[K] extends Store ? U : State[K] }>;
export function combine(
tuple: State,
fn: (tuple: { [K in keyof State]: State[K] extends Store ? U : State[K] }) => R,
config?: { skipVoid?: boolean },
): Store;
```
* **Features**
* Array order matches the order of passed stores.
* Updates are batched when multiple stores change simultaneously.
* Can mix stores with non-store values such as primitives or objects.
* When `fn` is provided:
* The transformation function `fn` must be pure.
* Function receives an array where order matches the input array order.
* If `fn` returns the same value as previous (by `!==` comparison), the store won't update.
* If `fn` throws an error during execution, the application will crash (this will be fixed in [effector v24](https://github.com/effector/effector/issues/1163)).
* Optional `config` parameter: see createStore for details on `skipVoid`.
* **Examples**
Without transformation function - creates an array store:
```ts
import { createStore, combine } from "effector";
const $x = createStore(10);
const $y = createStore(20);
const $z = createStore(30);
const $coordinates = combine([$x, $y, $z]);
$coordinates.watch((coords) => console.log(coords));
// => [10, 20, 30]
```
With transformation function - transforms array values:
```ts
import { createStore, combine } from "effector";
const $x = createStore(3);
const $y = createStore(4);
const $distance = combine([$x, $y], ([x, y]) => {
return Math.sqrt(x * x + y * y);
});
$distance.watch((dist) => console.log(`Distance: ${dist}`));
// => Distance: 5
```
Practical example - calculating totals from array:
```ts
import { createStore, combine } from "effector";
const $itemPrice1 = createStore(10);
const $itemPrice2 = createStore(25);
const $itemPrice3 = createStore(15);
const $cartTotal = combine([$itemPrice1, $itemPrice2, $itemPrice3], (prices) => {
return prices.reduce((sum, price) => sum + price, 0);
});
$cartTotal.watch((total) => console.log(`Total: $${total}`));
// => Total: $50
```
Array with different types:
```ts
import { createStore, combine } from "effector";
const $userName = createStore("Alice");
const $score = createStore(100);
const $isActive = createStore(true);
const $playerInfo = combine([$userName, $score, $isActive], ([name, score, active]) => {
return `Player ${name}: ${score} points (${active ? "online" : "offline"})`;
});
$playerInfo.watch((info) => console.log(info));
// => Player Alice: 100 points (online)
```
Mixing stores with primitives in array:
```ts
import { createStore, combine } from "effector";
const $currentPage = createStore(1);
const MAX_PAGES = 10;
const $pagination = combine([$currentPage, MAX_PAGES], ([current, max]) => {
return {
current,
max,
hasNext: current < max,
hasPrev: current > 1,
};
});
$pagination.watch((pagination) => console.log(pagination));
// => { current: 1, max: 10, hasNext: true, hasPrev: false }
```
* **Return value**
Returns a new derived store.
### Related API and Articles
* **API**
* Store API - Description of stores, their methods and properties
* createStore - Creating a new store
* sample - Key operator for building connections between units
* createEvent - Creating an event
* **Articles**
* Managing states in effector and how to use derived stores
# createApi
```ts
import { createApi } from "effector";
```
`createApi` is a shortcut for generating events connected to a store by supplying an object with for these events. If the source `store` is part of a domain, then the newly created events will also be within that domain.
## Methods
### `createApi(store, api)`
#### Formulae
```ts
createApi(store, api): objectWithEvents
```
#### Arguments
1. `store`
2. `api` (*Object*) An object with
#### Returns
(*Object*) An object with events
#### Examples
```js
import { createStore, createApi } from "effector";
const $playerPosition = createStore(0);
// Creating events and attaching them to the store
const api = createApi($playerPosition, {
moveLeft: (pos, offset) => pos - offset,
moveRight: (pos, offset) => pos + offset,
});
$playerPosition.watch((pos) => {
console.log("position", pos);
});
// => position 0
api.moveRight(10);
// => position 10
api.moveLeft(5);
// => position 5
```
Try it
# createDomain
```ts
import { createDomain, type Domain } from "effector";
```
## Methods
### `createDomain(name?)`
Creates a domain
#### Formulae
```typescript
createDomain(name?): Domain
```
#### Arguments
1. `name`? (*string*): domain name. Useful for debugging
#### Returns
: New domain
#### Examples
```js
import { createDomain } from "effector";
const domain = createDomain(); // Unnamed domain
const httpDomain = createDomain("http"); // Named domain
const statusCodeChanged = httpDomain.createEvent();
const downloadFx = httpDomain.createEffect();
const apiDomain = httpDomain.createDomain(); // nested domain
const $data = httpDomain.createStore({ status: -1 });
```
Try it
# createEffect
## createEffect
```ts
import { createEffect } from "effector";
const effectFx = createEffect();
```
Method for creating effects. Returns a new effect.
### How to Create Effects
The `createEffect` method supports several ways to create effects:
1. With a handler - this is the simplest way.
2. With configuration.
3. Without a handler, which can be set later using the .use(handler) method.
#### With Handler
* **Type**
```ts
createEffect(
handler: (params: Params) => Done | Promise,
): Effect
```
* **Example**
```ts
import { createEffect } from "effector";
const fetchUserReposFx = createEffect(async ({ name }) => {
const url = `https://api.github.com/users/${name}/repos`;
const req = await fetch(url);
return req.json();
});
fetchUserReposFx.done.watch(({ params, result }) => {
console.log(result);
});
await fetchUserReposFx({ name: "zerobias" });
```
#### With Configuration
The `name` field is used to improve error messages and debugging.
* **Type**
```ts
export function createEffect(config: {
name?: string;
handler?: (params: Params) => Promise | Done;
}): Effect;
```
* **Example**
```ts
import { createEffect } from "effector";
const fetchUserReposFx = createEffect({
name: "fetch user repositories",
async handler({ name }) {
const url = `https://api.github.com/users/${name}/repos`;
const req = await fetch(url);
return req.json();
},
});
await fetchUserReposFx({ name: "zerobias" });
```
#### Without Handler
Most commonly used for testing. More detailed information.
> WARNING use is an anti-pattern:
>
> Try to avoid using `.use()`, as it's an anti-pattern and degrades type inference.
* **Example**
```ts
import { createEffect } from "effector";
const fetchUserReposFx = createEffect();
fetchUserReposFx.use(async ({ name }) => {
const url = `https://api.github.com/users/${name}/repos`;
const req = await fetch(url);
return req.json();
});
await fetchUserReposFx({ name: "zerobias" });
```
### Examples
* **Updating state on effect completion**:
```ts
import { createStore, createEffect } from "effector";
interface Repo {
// ...
}
const $repos = createStore([]);
const fetchUserReposFx = createEffect(async (name: string) => {
const url = `https://api.github.com/users/${name}/repos`;
const req = await fetch(url);
return req.json();
});
$repos.on(fetchUserReposFx.doneData, (_, repos) => repos);
$repos.watch((repos) => {
console.log(`${repos.length} repos`);
});
// => 0 repos
await fetchUserReposFx("zerobias");
// => 26 repos
```
Run example
* **Watching effect state**:
```js
import { createEffect } from "effector";
const fetchUserReposFx = createEffect(async ({ name }) => {
const url = `https://api.github.com/users/${name}/repos`;
const req = await fetch(url);
return req.json();
});
fetchUserReposFx.pending.watch((pending) => {
console.log(`effect is pending?: ${pending ? "yes" : "no"}`);
});
fetchUserReposFx.done.watch(({ params, result }) => {
console.log(params); // {name: 'zerobias'}
console.log(result); // resolved value, result
});
fetchUserReposFx.fail.watch(({ params, error }) => {
console.error(params); // {name: 'zerobias'}
console.error(error); // rejected value, error
});
fetchUserReposFx.finally.watch(({ params, status, result, error }) => {
console.log(params); // {name: 'zerobias'}
console.log(`handler status: ${status}`);
if (error) {
console.log("handler rejected", error);
} else {
console.log("handler resolved", result);
}
});
await fetchUserReposFx({ name: "zerobias" });
```
Run example
### Common errors
Below is a list of possible errors you may encounter when working with effects:
* no handler used in \[effect name]
### Related API and Articles
* **API**
* Effect API - Description of effects, their methods and properties
* sample - Key operator for building connections between units
* attach - Creates new effects based on other effects
* **Articles**
* Working with effects
* How to type effects and other units
* Guide to testing effects and other units
# createEvent
## createEvent
```ts
import { createEvent } from "effector";
const event = createEvent();
```
Method for creating [events][eventApi].
### Formula
```ts
createEvent(eventName?: string): EventCallable
createEvent(config: {
name?: string
sid?: string
domain?: Domain
}): EventCallable
```
* **Arguments**
* `eventName`: Optional argument. Event name for debugging.
* `config`: Optional argument. Configuration object.
* `name`: Event name.
* `sid`: Stable identifier for SSR.
* `domain`: Domain for the event.
* **Return value**
Returns a new callable [event][eventTypes].
### Examples
Updating state by calling an event:
```js
import { createStore, createEvent } from "effector";
const addNumber = createEvent();
const $counter = createStore(0);
$counter.on(addNumber, (state, number) => state + number);
$counter.watch((state) => {
console.log("state", state);
});
// => 0
addNumber(10);
// => 10
addNumber(10);
// => 20
addNumber(10);
// => 30
```
Run example
We created the `addNumber` event and the `$counter` store, then subscribed to store updates.
Notice the function call `addNumber(10)`. Every time you call `addNumber(10)`, you can check the console and see how the state changes.
Processing data with derived events:
```js
import { createEvent } from "effector";
const extractPartOfArray = createEvent();
const array = extractPartOfArray.map((arr) => arr.slice(2));
array.watch((part) => {
console.log(part);
});
extractPartOfArray([1, 2, 3, 4, 5, 6]);
// => [3, 4, 5, 6]
```
Run example
### Common errors
Below is a list of possible errors you may encounter when working with events:
* call of derived event is not supported, use createEvent instead
* unit call from pure function is not supported, use operators like sample instead
### Related API and Articles
* **API**
* [`Event API`][eventApi] - Event API, its methods, properties and description
* [`createApi`][createApi] - Creating a set of events for a store
* [`merge`][merge] - Method for combining an array of units into one new event
* [`sample`][sample] - Connecting events with other units
* **Articles**
* [How to work with events][eventGuide]
* [How to think in effector and why events matter][mindset]
* [Guide to typing events and other units][typescript]
[eventApi]: /en/api/effector/Event
[eventTypes]: /en/api/effector/Event#event-types
[merge]: /en/api/effector/merge
[eventGuide]: /en/essentials/events
[mindset]: /en/resources/mindset
[typescript]: /en/essentials/typescript
[sample]: /en/api/effector/sample
[createApi]: /en/api/effector/createApi
# createStore
## createStore
```ts
import { createStore } from "effector";
const $store = createStore();
```
Method for creating [stores][storeApi].
### Formula
```ts
createStore(
defaultState: State, // Initial store state
config?: { // Configuration object with additional options
skipVoid?: boolean; // Controls updates with undefined values
name?: string; // Store name for debugging
sid?: string // Stable identifier for SSR
updateFilter?: (update: State, current: State) => boolean // Update filtering function
serialize?: // Serialization configuration for SSR
| 'ignore'
| {
write: (state: State) => SerializedState
read: (json: SerializedState) => State
}
domain?: Domain; // Domain to which the store belongs
},
): StoreWritable
```
* **Arguments**
1. **`defaultState`**: Initial state
2. **`config`**: Optional configuration object
* **`skipVoid`**: Optional argument. Determines whether the [store][storeApi] skips `undefined` values. Default is `true`. If you pass an `undefined` value to a store with `skipVoid: true`, you'll get [an error in the console][storeUndefinedError].
* **`name`**: Optional argument. Store name. [Babel-plugin][babel] can determine it from the store variable name if the name is not explicitly passed in the configuration.
* **`sid`**: Optional argument. Unique store identifier. [It's used to distinguish stores between different environments][storeSid]. When using [Babel-plugin][babel], it's set automatically.
* **`updateFilter`**:
Optional argument. A [pure function][pureFn] that prevents store updates if it returns `false`. Should be used when the standard update prevention (if the value to be written to the store equals `undefined` or the current store value) is insufficient.
* **`serialize`**: Optional argument responsible for store serialization.
* `'ignore'`: excludes the store from serialization when calling [serialize][serialize].
* Object with `write` and `read` methods for custom serialization. `write` is called when serialize is invoked and converts the store state to a JSON value – a primitive or simple object/array. `read` is called during fork if the provided `values` are the result of calling [serialize][serialize].
* **Return value**
Returns a new [store][storeApi].
### Examples
Basic store usage:
```js
import { createEvent, createStore } from "effector";
const addTodo = createEvent();
const clearTodos = createEvent();
const $todos = createStore([])
.on(addTodo, (todos, newTodo) => [...todos, newTodo])
.reset(clearTodos);
const $selectedTodos = $todos.map((todos) => {
return todos.filter((todo) => !!todo.selected);
});
$todos.watch((todos) => {
console.log("todos", todos);
});
```
Run example
Example with custom `serialize` configuration:
```ts
import { createEvent, createStore, serialize, fork, allSettled } from "effector";
const saveDate = createEvent();
const $date = createStore(null, {
// Date objects are automatically converted to ISO date strings when calling JSON.stringify
// but are not converted back to Date when calling JSON.parse – the result will be the same ISO date string
// This will cause state mismatch when hydrating state on the client during server-side rendering
//
// Custom `serialize` configuration solves this problem
serialize: {
write: (dateOrNull) => (dateOrNull ? dateOrNull.toISOString() : dateOrNull),
read: (isoStringOrNull) => (isoStringOrNull ? new Date(isoStringOrNull) : isoStringOrNull),
},
}).on(saveDate, (_, p) => p);
const serverScope = fork();
await allSettled(saveDate, { scope: serverScope, params: new Date() });
const serverValues = serialize(serverScope);
// `serialize.write` for store `$date` was called
console.log(serverValues);
// => { nq1e2rb: "2022-11-05T15:38:53.108Z" }
// Date object from store saved as ISO date
const clientScope = fork({ values: serverValues });
// `serialize.read` for store `$date` was called
const currentDate = clientScope.getState($date);
console.log(currentDate);
// => Date 11/5/2022, 10:40:13 PM
// ISO date string converted back to Date object
```
Run example
### Common Errors
Below is a list of possible errors you may encounter when working with stores:
* [`store: undefined is used to skip updates. To allow undefined as a value provide explicit { skipVoid: false } option`][storeUndefinedError].
* [`serialize: One or more stores dont have sids, their values are omitted`][serializeError].
* [`unit call from pure function is not supported, use operators like sample instead`][unitCallError].
### Related API and Articles
* **API**
* [`Store API`][storeApi] - Store API, its methods, properties and description
* [`createApi`][createApi] - Creating a set of events for a store
* [`combine`][combine] - Creating a new store based on other stores
* [`sample`][sample] - Connecting stores with other units
* **Articles**
* [How to manage state][storeGuide]
* [Guide to working with SSR][ssr]
* [What is SID and why stores need them][storeSid]
* [How to type stores and other units][typescript]
[storeApi]: /en/api/effector/Store
[storeUndefinedError]: /en/guides/troubleshooting#store-undefined
[storeSid]: /en/explanation/sids
[ssr]: /en/guides/server-side-rendering
[storeGuide]: /en/essentials/manage-states
[combine]: /en/api/effector/combine
[sample]: /en/api/effector/sample
[createApi]: /en/api/effector/createApi
[serialize]: /en/api/effector/serialize
[typescript]: /en/essentials/typescript
[babel]: /en/api/effector/babel-plugin
[pureFn]: /en/explanation/glossary/#purity
[unitCallError]: /en/guides/troubleshooting#unit-call-from-pure-not-supported
[serializeError]: /en/guides/troubleshooting/#store-without-sid
# createWatch
```ts
import { createWatch } from "effector";
```
## Methods
### `createWatch(config)`
Creates a subscription on unit (store, event, or effect).
#### Formulae
```ts
createWatch(config: {
unit: Unit
fn: (payload: T) => void
scope?: Scope
}): Subscription
```
#### Arguments
1. `config` (*Object*): Configuration
* `unit` (*Unit*): Target unit (store, event of effect) that will be watched
* `fn` (*Function*): Function that will be called when the unit is triggered. Accepts the unit's payload as the first argument.
* `scope` (): An optional scope object (forked instance) to restrict watcher calls on particular scope.
#### Returns
: Unsubscribe function
#### Examples
##### With scope
```js
import { createWatch, createEvent, fork, allSettled } from "effector";
const changeName = createEvent();
const scope = fork();
const unwatch = createWatch({ unit: changeName, scope, fn: console.log });
await allSettled(changeName, { scope, params: "John" }); // output: John
changeName("John"); // no output
```
##### Without scope
```js
import { createWatch, createEvent, fork, allSettled } from "effector";
const changeName = createEvent();
const scope = fork();
const unwatch = createWatch({ unit: changeName, fn: console.log });
await allSettled(changeName, { scope, params: "John" }); // output: John
changeName("John"); // output: John
```
# debug traces
## Debug Trace import
```ts
import "effector/enable_debug_traces";
```
A special import that enables detailed traces for difficult-to-debug errors, such as a Store missing a proper SID during Scope serialization.
> WARNING Performance cost:
>
> Debug traces work by capturing additional information when Stores and Events are created.
> This introduces a performance overhead during module initialization.
>
> We do not recommend using this API in production environments.
To enable debug traces, add `import "effector/enable_debug_traces"` to the entrypoint of your bundle, like this:
```ts
// src/index.ts
import "effector/enable_debug_traces";
// ...rest of your code
```
### When to use it
If you encounter an error that can be diagnosed with this API, you will see a recommendation in the console: `Add "import 'effector/enable_debug_traces'" to your code entry module to see full stack traces`.
Don't forget to remove this import once the issue has been resolved.
# fork API
[scopeApi]: /en/api/effector/Scope
[domainApi]: /en/api/effector/Domain
[serializeApi]: /en/api/effector/serialize
[allSettledApi]: /en/api/effector/allSettled
[hydrateApi]: /en/api/effector/hydrate
[sampleApi]: /en/api/effector/sample
[storeApi]: /en/api/effector/Store
[effectApi]: /en/api/effector/Effect
## `fork` API
```ts
import { fork, type Scope } from "effector";
```
`fork` creates an isolated [scope][scopeApi] of your app. Use it for SSR, testing, or any case where you need to run logic in a safe copy without touching global units.
### How it works
1. Call `fork` to get a new [scope][scopeApi].
2. If `values` or `handlers` are provided, they are applied when the [scope][scopeApi] is created.
3. Units run in that [scope][scopeApi] operate on isolated state and overridden handlers.
4. Read state via `scope.getState(store)` or serialize it with [`serialize`][serializeApi].
### `fork` configuration forms
| Form
| Description |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| fork() | Creates a fresh [scope][scopeApi] with no prefilled data or handler overrides. |
| fork({ values?, handlers? }) | Creates a [scope][scopeApi] with initial store values and custom effect handlers. |
| fork(domain, options?) | Legacy form that requires `domain`. Prefer `fork({ values?, handlers? })` unless you need old signature compatibility. |
### Configurations
#### `fork()`
Creates a new application [scope][scopeApi] with default behavior.
* **Formula**
```ts
fork(): Scope
```
* **Type**
```ts
type SerializedState = Record;
type StorePair = [StoreWritable, T];
export function fork(config?: {
values?: StorePair[] | SerializedState;
handlers?: Array<[Effect, Function]>;
}): Scope;
```
* **Notes**
* Returns a fresh [scope][scopeApi] without prefilled stores or handler overrides.
* Use when you need a clean state (e.g., first render on the server).
* **Example**
```ts
import { createStore, createEvent, fork, allSettled } from "effector";
const inc = createEvent();
const dec = createEvent();
const $counter = createStore(0);
$counter.on(inc, (value) => value + 1);
$counter.on(dec, (value) => value - 1);
const scopeA = fork();
const scopeB = fork();
await allSettled(inc, { scope: scopeA });
await allSettled(dec, { scope: scopeB });
console.log($counter.getState()); // => 0
console.log(scopeA.getState($counter)); // => 1
console.log(scopeB.getState($counter)); // => -1
```
* **Returns**
A new [scope][scopeApi].
#### `fork({ values?, handlers? })`
Adds initial store values and overrides effect handlers inside the [scope][scopeApi].
* **Formula**
```ts
fork({
values?, // e.g. [[$store, value], ...] or serialized object
handlers?, // e.g. [[effect, handler], ...]
}): Scope
```
* **Type**
```ts
type SerializedState = Record;
type StorePair = [StoreWritable, T];
export function fork(config?: {
values?: StorePair[] | SerializedState;
handlers?: Array<[Effect, Function]>;
}): Scope;
```
* **Notes**
* `values` can be an array of tuples `[$store, value]` or a serialized state object (usually from [`serialize`][serializeApi]); tuples are preferred.
* `handlers` accepts only an array of tuples to override effect handlers; overrides apply only within the created [scope][scopeApi].
* Values and handlers are applied once at [scope][scopeApi] creation and are not updated automatically.
* **Examples**
Set initial state and replace a handler in a test:
```ts
import { createEffect, createStore, fork, allSettled } from "effector";
const fetchFriendsFx = createEffect<{ limit: number }, string[]>(async ({ limit }) => {
return [];
});
const $user = createStore("guest");
const $friends = createStore([]);
$friends.on(fetchFriendsFx.doneData, (_, result) => result);
const testScope = fork({
values: [[$user, "alice"]],
handlers: [[fetchFriendsFx, () => ["bob", "carol"]]],
});
await allSettled(fetchFriendsFx, {
scope: testScope,
params: { limit: 10 },
});
console.log(testScope.getState($friends));
// => ['bob', 'carol']
```
Create a [scope][scopeApi] from serialized state:
```ts
import { fork } from "effector";
const serialized = {
userSid: "alice",
ageSid: 21,
};
const scope = fork({ values: serialized });
```
* **Returns**
A new [scope][scopeApi] with applied `values` and `handlers`.
#### `fork(domain, options?)`
Legacy form that requires a [domain][domainApi]; keep only for compatibility with older code.
> ERROR Deprecated:
>
> Use `fork({ values?, handlers? })` because `fork` tracks units automatically without a `domain`.
* **Formula**
```ts
fork(domain, {
values?, // e.g. [[$store, value], ...] or serialized object
handlers?, // e.g. [[effect, handler], ...]
}): Scope
```
* **Type**
```ts
type SerializedState = Record;
export function fork(
domain: Domain,
config?: {
values?: SerializedState | Array<[StoreWritable, any]>;
handlers?: Array<[Effect, Function]>;
},
): Scope;
```
* **Notes**
* Pass `domain` only if your project still depends on the old signature.
* Allowed `values` and `handlers` formats match the configuration without `domain`.
* **Example**
```ts
import { createDomain, createStore, fork } from "effector";
const app = createDomain();
const $flag = app.createStore(false);
const scope = fork(app, {
values: [[$flag, true]],
});
console.log(scope.getState($flag)); // => true
```
* **Returns**
A new [scope][scopeApi] attached to the provided [domain][domainApi].
### Related API and articles
* **API**
* Scope — structure of isolated state
* allSettled — run an effect inside a [scope][scopeApi] and await completion
* serialize — serialize [scope][scopeApi] state
* hydrate — restore state into a [scope][scopeApi]
* **Articles**
* SSR and working with scope
* Testing with an isolated scope
* Scope context loss and recovery
# forward
## Methods
> ERROR Deprecated:
>
> Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0).
>
> The core team recommends using sample instead of `forward`.
```ts
import { forward, type Subscription } from "effector";
```
Method to create connection between units in a declarative way. Send updates from one set of units to another.
### `forward({ from, to })`
#### Formulae
```ts
forward({
from: Unit | Unit[],
to: Unit | Unit[]
}): Subscription
```
#### Arguments
1. `from` (Unit | Unit\[]): Source of updates. Forward will listen for changes of these units
* if an [*Event*][_Event_] is passed, `to` will be triggered on each event trigger and receives event argument
* if a [*Store*][_Store_] is passed, `to` will be triggered on each store **change** and receives new value of the store
* if an [*Effect*][_Effect_] is passed, `to` will be triggered on each effect call and receives effect parameter
* if an array of units is passed, `to` will be triggered when any unit in `from` array is triggered
2. `to` (Unit | Unit\[]): Target for updates. `forward` will trigger these units with data from `from`
* if passed an [*Event*][_Event_], it will be triggered with data from `from` unit
* if passed a [*Store*][_Store_], data from `from` unit will be written to store and **trigger its update**
* if passed an [*Effect*][_Effect_], it will be called with data from `from` unit as parameter
* if `to` is an array of units, each unit in that array will be triggered
#### Returns
Subscription: Unsubscribe function. It breaks connection between `from` and `to`. After call, `to` will not be triggered anymore.
> INFO since:
>
> Arrays of units are supported since [effector 20.6.0](https://changelog.effector.dev/#effector-20-6-0)
#### Examples
##### Send store updates to another store
```js
import { createStore, createEvent, forward } from "effector";
const $store = createStore(1);
const event = createEvent();
forward({
from: event,
to: $store,
});
$store.watch((state) => console.log("store changed: ", state));
// => store changed: 1
event(200);
// => store changed: 200
```
Try it
##### Forward between arrays of units
```js
import { createEvent, forward } from "effector";
const firstSource = createEvent();
const secondSource = createEvent();
const firstTarget = createEvent();
const secondTarget = createEvent();
forward({
from: [firstSource, secondSource],
to: [firstTarget, secondTarget],
});
firstTarget.watch((e) => console.log("first target", e));
secondTarget.watch((e) => console.log("second target", e));
firstSource("A");
// => first target A
// => second target A
secondSource("B");
// => first target B
// => second target B
```
Try it
[_effect_]: /en/api/effector/Effect
[_store_]: /en/api/effector/Store
[_event_]: /en/api/effector/Event
# fromObservable
```ts
import { fromObservable, type Observable } from "effector";
```
## Methods
### `fromObservable()`
Creates an event containing all items from an Observable.
#### Formulae
```ts
fromObservable(source: Observable): Event
```
#### Arguments
1. `observable` (*Observable*)
#### Returns
: New event
#### Examples
##### Basic use case
```js
import { interval } from "rxjs";
import { fromObservable } from "effector";
//emit value in sequence every 1 second
const source = interval(1000);
const event = fromObservable(source);
//output: 0,1,2,3,4,5....
event.watch(console.log);
```
# guard
```ts
import { guard } from "effector";
```
> WARNING Deprecated:
>
> Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0).
>
> The core team recommends using sample instead of `guard`.
Method for conditional event routing.
It provides a way to control one dataflow with the help of another: when the condition and the data are in different places, we can use `guard` with stores as filters to trigger events when condition state is true, thereby modulate signals without mixing them.
## Methods
### `guard({ clock?, source?, filter, target? })`
#### Formulae
```ts
guard({ clock?, source?, filter, target? }): target
```
> INFO:
>
> Either `clock` or `source` is required
When `clock` is triggered, check `filter` for [truthy] and call `target` with data from `source` if `true`.
* If `clock` is not passed, `guard` will be triggered on every `source` update
* If `source` is not passed, call `target` with data from `clock`
* If `target` is not passed, create with type of `source` and return it from `guard()`
* If `filter` is , check it value for [truthy]
* If `filter` is `Function`, call it with data from `source` and check result for [truthy]
[truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy
> INFO since:
>
> `clock` in `guard` is available since [effector 21.8.0](https://changelog.effector.dev/#effector-21-8-0)
### `guard({source, filter, target?})`
#### Arguments
1. `params` (*Object*): Configuration object
#### Returns
, which fires upon `clock` trigger
#### Examples
##### Basic
```js
import { createStore, createEffect, createEvent, guard } from "effector";
const clickRequest = createEvent();
const fetchRequest = createEffect((n) => new Promise((rs) => setTimeout(rs, 2500, n)));
const $clicks = createStore(0).on(clickRequest, (x) => x + 1);
const $requestsCount = createStore(0).on(fetchRequest, (x) => x + 1);
const $isIdle = fetchRequest.pending.map((pending) => !pending);
/*
1. on clickRequest
2. if $isIdle is true
3. take current $clicks value
4. and call fetchRequest with it
*/
guard({
clock: clickRequest /* 1 */,
filter: $isIdle /* 2 */,
source: $clicks /* 3 */,
target: fetchRequest /* 4 */,
});
```
See ui visualization
##### Function predicate
```js
import { createEffect, createEvent, guard } from "effector";
const submitForm = createEvent();
const searchUser = createEffect();
guard({
source: submitForm,
filter: (user) => user.length > 0,
target: searchUser,
});
submitForm(""); // nothing happens
submitForm("alice"); // ~> searchUser('alice')
```
Try it
### `guard(source, {filter: booleanStore})`
#### Arguments
1. `source` (//): Source unit. Will trigger given `guard` on updates
2. `filter` (): Filter store
#### Examples
##### Store filter
```js
import { createEvent, createStore, createApi, guard } from "effector";
const trigger = createEvent();
const $unlocked = createStore(true);
const { lock, unlock } = createApi($unlocked, {
lock: () => false,
unlock: () => true,
});
const target = guard(trigger, {
filter: $unlocked,
});
target.watch(console.log);
trigger("A");
lock();
trigger("B"); // nothing happens
unlock();
trigger("C");
```
Try it
### `guard(source, {filter: predicate})`
#### Arguments
1. `source` (//): Source unit. Will trigger given `guard` on updates
2. `filter` (*(payload) => Boolean*): Predicate function, should be
#### Examples
##### Predicate function
```js
import { createEvent, guard } from "effector";
const source = createEvent();
const target = guard(source, {
filter: (x) => x > 0,
});
target.watch(() => {
console.log("target called");
});
source(0);
// nothing happens
source(1);
// target called
```
Try it
# hydrate
```ts
import { hydrate } from "effector";
```
A companion method for . Hydrates provided values into corresponding stores within a provided domain or scope. The main purpose is an application state hydration on the client side after SSR.
## Methods
### `hydrate(domainOrScope, {values})`
> WARNING:
>
> You need to make sure that the store is created beforehand, otherwise, the hydration might fail. This could be the case if you keep store initialization/hydration scripts separate from stores' creation.
#### Formulae
```ts
hydrate(domainOrScope: Domain | Scope, { values: Map, any> | {[sid: string]: any} }): void
```
#### Arguments
1. `domainOrScope`: domain or scope which will be filled with given `values`
2. `values`: a mapping from store sids to store values or a Map where keys are store objects and values contain initial store value
#### Returns
`void`
#### Examples
Populate store with a predefined value
```js
import { createStore, createDomain, fork, serialize, hydrate } from "effector";
const domain = createDomain();
const $store = domain.createStore(0);
hydrate(domain, {
values: {
[$store.sid]: 42,
},
});
console.log($store.getState()); // 42
```
Try it
# effector
## Effector API
Effector API reference:
### Unit Definitions
* Event\
* Effect\
* Store\
* Domain
* Scope
### Unit Creators
* createEvent()
* createStore(default)
* createEffect(handler)
* createDomain()
### Common Methods
* combine(...stores, f)
* attach({effect, mapParams, source})
* sample({clock, source, fn, target})
* merge(\[eventA, eventB])
* split(event, cases)
* createApi(store, api)
### Fork API
* fork()
* serialize(scope)
* allSettled(unit, { scope })
* scopeBind(event)
* hydrate(domain)
### Plugins
* effector/babel-plugin
* @effector-swc-plugin
### Utilities
* is
* fromObservable(observable)
### Low Level API
* clearNode()
* withRegion()
* launch()
* inspect()
### Import Map
Package `effector` provides couple different entry points for different purposes:
* effector/compat
* effector/inspect
* effector/babel-plugin
### Deprecated Methods
* forward({from, to})
* guard({source, filter, target})
# inspect
```ts
import { inspect } from "effector/inspect";
```
Special API methods designed to handle debugging and monitoring use cases without giving too much access to internals of your actual app.
Useful to create developer tools and production monitoring and observability instruments.
## Inspect API
Allows us to track any computations that have happened in the effector's kernel.
### `inspect()`
#### Example
```ts
import { inspect, type Message } from "effector/inspect";
import { someEvent } from "./app-code";
function logInspectMessage(m: Message) {
const { name, value, kind } = m;
return console.log(`[${kind}] ${name} ${value}`);
}
inspect({
fn: (m) => {
logInspectMessage(m);
},
});
someEvent(42);
// will log something like
// [event] someEvent 42
// [on] 42
// [store] $count 1337
// ☝️ let's say that reducer adds 1295 to provided number
//
// and so on, any triggers
```
Scope limits the extent to which computations can be tracked. If no scope is provided - default out-of-scope mode computations will be tracked.
```ts
import { fork, allSettled } from "effector";
import { inspect, type Message } from "effector/inspect";
import { someEvent } from "./app-code";
function logInspectMessage(m: Message) {
const { name, value, kind } = m;
return console.log(`[${kind}] ${name} ${value}`);
}
const myScope = fork();
inspect({
scope: myScope,
fn: (m) => {
logInspectMessage(m);
},
});
someEvent(42);
// ☝️ No logs! That's because tracking was restricted by myScope
allSettled(someEvent, { scope: myScope, params: 42 });
// [event] someEvent 42
// [on] 42
// [store] $count 1337
```
### Tracing
Adding `trace: true` setting allows looking up previous computations, that led to this specific one. It is useful to debug the specific reason for some events happening
#### Example
```ts
import { fork, allSettled } from "effector";
import { inspect, type Message } from "effector/inspect";
import { someEvent, $count } from "./app-code";
function logInspectMessage(m: Message) {
const { name, value, kind } = m;
return console.log(`[${kind}] ${name} ${value}`);
}
const myScope = fork();
inspect({
scope: myScope,
trace: true, // <- explicit setting is needed
fn: (m) => {
if (m.kind === "store" && m.sid === $count.sid) {
m.trace.forEach((tracedMessage) => {
logInspectMessage(tracedMessage);
// ☝️ here we are logging the trace of specific store update
});
}
},
});
allSettled(someEvent, { scope: myScope, params: 42 });
// [on] 42
// [event] someEvent 42
// ☝️ traces are provided in backwards order, because we are looking back in time
```
### Errors
Effector does not allow exceptions in pure functions. In such case, branch computation is stopped and an exception is logged. There is also a special message type in such case:
#### Example
```ts
inspect({
fn: (m) => {
if (m.type === "error") {
// do something about it
console.log(`${m.kind} ${m.name} computation has failed with ${m.error}`);
}
},
});
```
## Inspect Graph
Allows us to track declarations of units, factories, and regions.
### Example
```ts
import { createStore } from "effector";
import { inspectGraph, type Declaration } from "effector/inspect";
function printDeclaration(d: Declaration) {
console.log(`${d.kind} ${d.name}`);
}
inspectGraph({
fn: (d) => {
printDeclaration(d);
},
});
const $count = createStore(0);
// logs "store $count" to console
```
### `withRegion`
Meta-data provided via region's root node is available on declaration.
#### Example
```ts
import { createNode, withRegion, createStore } from "effector";
import { inspectGraph, type Declaration } from "effector/inspect";
function createCustomSomething(config) {
const $something = createStore(0);
withRegion(createNode({ meta: { hello: "world" } }), () => {
// some code
});
return $something;
}
inspectGraph({
fn: (d) => {
if (d.type === "region") console.log(d.meta.hello);
},
});
const $some = createCustomSomething({});
// logs "world"
```
# is
```ts
import { is, type Unit } from "effector";
```
Namespace for unit validators.
## Methods
### `is.store(value)`
Checks if given value is
#### Returns
`boolean` — Type-guard
#### Examples
```js
import { is, createStore, createEvent, createEffect, createDomain } from "effector";
const $store = createStore(null);
const event = createEvent();
const fx = createEffect();
is.store($store);
// => true
is.store(event);
// => false
is.store(fx);
// => false
is.store(createDomain());
// => false
is.store(fx.pending);
// => true
is.store(fx.done);
// => false
is.store($store.updates);
// => false
is.store(null);
// => false
```
Try it
### `is.event(value)`
Checks if given value is
#### Returns
`boolean` — Type-guard
#### Examples
```js
import { is, createStore, createEvent, createEffect, createDomain } from "effector";
const $store = createStore(null);
const event = createEvent();
const fx = createEffect();
is.event($store);
// => false
is.event(event);
// => true
is.event(fx);
// => false
is.event(createDomain());
// => false
is.event(fx.pending);
// => false
is.event(fx.done);
// => true
is.event($store.updates);
// => true
is.event(null);
// => false
```
Try it
### `is.effect(value)`
Checks if given value is
#### Returns
`boolean` — Type-guard
#### Examples
```js
import { is, createStore, createEvent, createEffect, createDomain } from "effector";
const $store = createStore(null);
const event = createEvent();
const fx = createEffect();
is.effect($store);
// => false
is.effect(event);
// => false
is.effect(fx);
// => true
is.effect(createDomain());
// => false
is.effect(null);
// => false
```
Try it
### `is.targetable`
Checks if given value can be used in operators target (or be called as a function in case of events)
#### Returns
`boolean` — Type-guard
#### Examples
```js
import { is, createStore, createEvent, createEffect } from "effector";
const $store = createStore(null);
const $mapped = $store.map((x) => x);
const event = createEvent();
const mappedEvent = event.map((x) => x);
const fx = createEffect();
is.targetable($store);
// => true
is.targetable($mapped);
// => false
is.targetable(event);
// => true
is.targetable(mappedEvent);
// => false
is.targetable(fx);
// => true
```
### `is.domain(value)`
Checks if given value is
#### Returns
`boolean` — Type-guard
#### Examples
```js
import { is, createStore, createEvent, createEffect, createDomain } from "effector";
const $store = createStore(null);
const event = createEvent();
const fx = createEffect();
is.domain($store);
// => false
is.domain(event);
// => false
is.domain(fx);
// => false
is.domain(createDomain());
// => true
is.domain(null);
// => false
```
Try it
### `is.scope(value)`
> INFO since:
>
> [effector 22.0.0](https://changelog.effector.dev/#effector-22-0-0)
Checks if given value is since [effector 22.0.0](https://changelog.effector.dev/#effector-22-0-0).
#### Returns
`boolean` — Type-guard
#### Examples
```js
import { fork } from "effector";
const $store = createStore(null);
const event = createEvent();
const fx = createEffect();
const scope = fork();
is.scope(scope);
// => true
is.scope($store);
// => false
is.scope(event);
// => false
is.scope(fx);
// => false
is.scope(createDomain());
// => false
is.scope(null);
// => false
```
Try it
### `is.unit(value)`
Checks if given value is Unit: Store, Event, Effect, Domain or Scope
#### Returns
`boolean` — Type-guard
#### Examples
```js
import { is, createStore, createEvent, createEffect, createDomain, fork } from "effector";
const $store = createStore(null);
const event = createEvent();
const fx = createEffect();
const scope = fork();
is.unit(scope);
// => true
is.unit($store);
// => true
is.unit(event);
// => true
is.unit(fx);
// => true
is.unit(createDomain());
// => true
is.unit(fx.pending);
// => true
is.unit(fx.done);
// => true
is.unit($store.updates);
// => true
is.unit(null);
// => false
```
Try it
### `is.attached(value)`
> INFO since:
>
> [effector 22.4.0](https://changelog.effector.dev/#effector-22-4-0)
Checks if given value is created via method. If passed not an effect, returns `false`.
#### Returns
`boolean` — Type-guard
#### Usage
Sometimes you need to add an error log on effects failures, but only on effects that have been "localized" via `attach`.
If you leave `onCreateEffect` as it is, without checks, the error log will be duplicated, because it will happen on the parent and the child effect.
```js
import { createDomain, attach, is } from "effector";
const logFailuresDomain = createDomain();
logFailuresDomain.onCreateEffect((effect) => {
if (is.attached(effect)) {
effect.fail.watch(({ params, error }) => {
console.warn(`Effect "${effect.compositeName.fullName}" failed`, params, error);
});
}
});
const baseRequestFx = logFailuresDomain.createEffect((path) => {
throw new Error(`path ${path}`);
});
const loadDataFx = attach({
mapParams: () => "/data",
effect: baseRequestFx,
});
const loadListFx = attach({
mapParams: () => "/list",
effect: baseRequestFx,
});
loadDataFx();
loadListFx();
```
Try it
#### Examples
```js
import { is, createStore, createEvent, createEffect, createDomain, attach } from "effector";
const $store = createStore(null);
const event = createEvent();
const fx = createEffect();
const childFx = attach({
effect: fx,
});
is.attached(childFx);
// => true
is.attached(fx);
// => false
is.attached($store);
// => false
is.attached(event);
// => false
is.attached(createDomain());
// => false
is.attached(null);
// => false
```
Try it
# launch
```ts
import { launch, type Unit, type Node } from "effector";
```
> INFO since:
>
> [effector 20.10.0](https://changelog.effector.dev/#effector-20-10-0)
## Methods
### `launch({ target, params })`
Low level method for running computation in units (events, effects or stores). Mostly used by library developers for fine-grained control of computations.
#### Formulae
```ts
launch({
target,
params,
defer?: boolean,
page?: any,
scope?: Scope,
meta?: Record,
}): void
```
#### Arguments
TBD
#### Returns
`void`
### `launch(unit, params)`
#### Formulae
```ts
launch(unit: Unit | Node, params: T): void
```
#### Returns
`void`
# merge
```ts
import { merge, type Unit } from "effector";
```
## Methods
### `merge(units)`
> INFO since:
>
> [effector 20.0.0](https://changelog.effector.dev/#effector-20-0-0)
Merges an array of units (events, effects, or stores), returning a new event that triggers upon any of the given units being triggered.
```ts
merge(units: Unit[]): Event
```
#### Arguments
1. `units`: An array of units to be merged.
#### Returns
: A new event that fires when any of the given units is triggered.
> TIP:
>
> In the case of a store, the resulting event will fire upon store updates.
#### Types
TBD
#### Examples
##### Basic Usage
```js
import { createEvent, merge } from "effector";
const foo = createEvent();
const bar = createEvent();
const baz = merge([foo, bar]);
baz.watch((v) => console.log("merged event triggered: ", v));
foo(1);
// => merged event triggered: 1
bar(2);
// => merged event triggered: 2
```
Try it
##### Working with Stores
```js
import { createEvent, createStore, merge } from "effector";
const setFoo = createEvent();
const setBar = createEvent();
const $foo = createStore(0).on(setFoo, (_, v) => v);
const $bar = createStore(100).on(setBar, (_, v) => v);
const anyUpdated = merge([$foo, $bar]);
anyUpdated.watch((v) => console.log(`state changed to: ${v}`));
setFoo(1); // => state changed to: 1
setBar(123); // => state changed to: 123
```
Try it
##### Merging a Store and an Event
```js
import { createEvent, createStore, merge } from "effector";
const setFoo = createEvent();
const otherEvent = createEvent();
const $foo = createStore(0).on(setFoo, (_, v) => v);
const merged = merge([$foo, otherEvent]);
merged.watch((v) => console.log(`merged event payload: ${v}`));
setFoo(999);
// => merged event payload: 999
otherEvent("bar");
// => merged event payload: bar
```
Try it
# effector/babel-plugin
Since Effector allows to automate many common tasks (like setting Stable IDentifiers and providing debug information for Units), there is a built-in plugin for Babel that enhances the developer experience when using the library.
## Usage
Please refer to the Babel plugin documentation for usage examples.
# effector/compat
```ts
import {} from "effector/compat";
```
The library provides a separate module with compatibility up to IE11 and Chrome 47 (browser for Smart TV devices).
> WARNING Bundler, Not Transpiler:
>
> Since third-party libraries can import `effector` directly, you **should not** use transpilers like Babel to replace `effector` with `effector/compat` in your code because by default, Babel will not transform third-party code.
>
> **Use a bundler instead**, as it will replace `effector` with `effector/compat` in all modules, including those from third parties.
### Required Polyfills
You need to install polyfills for these objects:
* `Promise`
* `Object.assign`
* `Array.prototype.flat`
* `Map`
* `Set`
In most cases, a bundler can automatically add polyfills.
#### Vite
Vite Configuration Example
```js
import { defineConfig } from "vite";
import legacy from "@vitejs/plugin-legacy";
export default defineConfig({
plugins: [
legacy({
polyfills: ["es.promise", "es.object.assign", "es.array.flat", "es.map", "es.set"],
}),
],
});
```
## Usage
### Manual Replacement
You can use `effector/compat` instead of the `effector` package if you need to support old browsers.
```diff
- import {createStore} from 'effector'
+ import {createStore} from 'effector/compat'
```
### Automatic Replacement
However, you can set up your bundler to automatically replace `effector` with `effector/compat` in your code.
#### Webpack
Webpack Configuration Example
```js
module.exports = {
resolve: {
alias: {
effector: "effector/compat",
},
},
};
```
#### Vite
Vite Configuration Example
```js
import { defineConfig } from "vite";
export default defineConfig({
resolve: {
alias: {
effector: "effector/compat",
},
},
});
```
# effector/inspect
## `effector/inspect`
Effector has special API methods designed to handle debugging and monitoring use cases without giving too much access to the internals of your actual app — Inspect API.
### Why a Separate Module?
Inspect API is designed to be disposable. By design, any feature that uses Inspect API can be removed from the production build without any side effects. To emphasize this, Inspect API is not included in the main module. Instead, it's available in a separate module `effector/inspect`.
### Usage
Please refer to Inspect API docs for usage examples.
# restore
```ts
import { restore } from "effector";
```
## Methods
### `restore(event, defaultState)`
Creates a from an . It works like a shortcut for `createStore(defaultState).on(event, (_, payload) => payload)`
> WARNING It is not a derived store:
>
> Restore creates a new store. It is not a DerivedStore. That means you can modify its state via events, and use it as `target` in sample.
#### Formulae
```ts
restore(event: Event, defaultState: T): StoreWritable
```
#### Arguments
1. `event`
2. `defaultState` (*Payload*)
#### Returns
: New store
#### Examples
##### Basic
```js
import { createEvent, restore } from "effector";
const event = createEvent();
const $store = restore(event, "default");
$store.watch((state) => console.log("state: ", state));
// state: default
event("foo");
// state: foo
```
Try it
### `restore(effect, defaultState)`
Creates a out of successful results of an . It works like a shortcut for `createStore(defaultState).on(effect.done, (_, {result}) => result)`
#### Formulae
```ts
restore(effect: Effect, defaultState: Done): StoreWritable
```
#### Arguments
1. `effect`
2. `defaultState` (*Done*)
#### Returns
: New store
#### Types
Store will have the same type as `Done` from `Effect`. Also, `defaultState` should have `Done` type.
#### Examples
##### Effect
```js
import { createEffect, restore } from "effector";
const fx = createEffect(() => "foo");
const $store = restore(fx, "default");
$store.watch((state) => console.log("state: ", state));
// => state: default
await fx();
// => state: foo
```
Try it
### `restore(shape)`
Creates an object with stores from an object with values.
#### Formulae
TBD
#### Arguments
1. `shape` (*State*)
#### Returns
: New store.
#### Examples
##### Object
```js
import { restore } from "effector";
const { foo: $foo, bar: $bar } = restore({
foo: "foo",
bar: 0,
});
$foo.watch((foo) => {
console.log("foo", foo);
});
// => foo 'foo'
$bar.watch((bar) => {
console.log("bar", bar);
});
// => bar 0
```
Try it
# sample API
[units]: /en/explanation/glossary#common-unit
[eventApi]: /en/api/effector/Event
[storeApi]: /en/api/effector/Store
[effectApi]: /en/api/effector/Effect
[purity]: /en/explanation/glossary/#purity
## `sample` API
```ts
import { sample } from "effector";
```
The `sample` method is used to connect units. Its main purpose is to take data from one place `source` and send it to another `target` when a certain trigger `clock` occurs.
A common use case is when you need to process an event using data from a store. Instead of using `store.getState()`, which can lead to inconsistent state, it's better to use `sample`.
> TIP how to work with sample:
>
> Learn how to compose units and use the
### How it works
* When `clock` triggers, the value from `source` is read.
* If a `filter` is specified and returns `true`, or if it's a store with `true` value, processing continues.
* If a `fn` is provided, data is transformed.
* Data is then passed to the `target`.
### Special behavior of `sample`
* If `clock` is not provided, `sample` will trigger on every update of `source`.
* If `target` is not provided, `sample` will create and return a new derived [unit][units].
### Returned unit and value
If `target` is not provided, it will be created at runtime. The type of unit returned depends on this table:
| clock \ source | | | |
| ----------------------------------- | --------------------------------- | --------------------------------- | ----------------------------------- |
| | `Store` | `Event` | `Event` |
| | `Event` | `Event` | `Event` |
| | `Event` | `Event` | `Event` |
How to use this table:
1. Pick the type of `clock` (column).
2. Pick the type of `source` (row).
3. The intersecting cell shows the return type.
If `target` is explicitly provided, then that `target` is returned.
Example:
```ts
const event = createEvent();
const $store = createStore();
const $secondStore = createStore();
const $derivedStore = sample({
clock: $store,
source: $secondStore,
});
// Returns a derived store because both clock and source are stores
const derivedEvent = sample({
clock: event,
source: $store,
});
// Returns a derived event because the clock is an event
```
### Full form
* **Formula**
```ts
sample({
clock?, // trigger
source?, // data source
filter?, // filter predicate
fn?, // transformation function
target?, // target unit
batch?, // batching flag
name? // unit name
})
```
#### `clock`
A trigger unit that determines when to sample the source.
Optional.
* **Type**
```ts
sample({
clock?: Unit | Unit[],
})
```
Can be:
* [`Event`][eventApi] — triggers on event call
* [`Store`][storeApi] — triggers on store update
* [`Effect`][effectApi] — triggers on effect execution
* `Unit[]` — triggers when any unit in the array is triggered
> INFO either clock or source required:
>
> Although the `clock` argument is optional, when using the `sample` method you must provide either `clock` or source.
```ts
const clicked = createEvent();
const $store = createStore(0);
const fetchFx = createEffect();
sample({
source: $data,
clock: clicked,
});
sample({
source: $data,
clock: $store,
});
sample({
source: $data,
clock: [clicked, fetchFx.done],
});
```
***
#### `source`
The data source to be read when the `clock` unit triggers.
If `clock` is not provided, then `source` is used as the `clock`.
Optional.
* **Type**
```ts
sample({
source?: Unit | Unit[] | { [key: string]: Unit },
})
```
Can be:
* [`Store`][storeApi] — reads the current value of the store
* [`Event`][eventApi] — takes the most recent payload from the event
* [`Effect`][effectApi] — takes the most recent payload from the effect call
* Object of [units][units] — for combining multiple sources
* Array of [units][units] — for combining multiple sources
> INFO either source or clock required:
>
> Although the `source` argument is optional, when using the `sample` method you must provide either `source` or clock.
***
#### `filter`
A predicate function or store used to filter the data. If it returns `false` (or is a store that holds `false`), the data will not be passed to `target`.
Optional.
* **Type**
```ts
sample({
filter?: Store | (source: Source, clock: Clock) => (boolean | Store),
})
```
Can be:
* [`Store`][storeApi] — a boolean store (either base or derived)
* Predicate function — returns a `boolean` value
```ts
const $isUserActive = createStore(false);
sample({
clock: checkScore,
source: $score,
filter: (score) => score > 100,
target: showWinnerFx,
});
sample({
clock: action,
source: $user,
filter: $isUserActive,
target: adminActionFx,
});
```
***
#### `fn`
A function used to transform the data before passing it to the `target`.
The function **must be pure**.
Optional.
* **Type**
```ts
sample({
fn?: (source: Source, clock: Clock) => Target
})
```
> INFO returned data type:
>
> The type of data returned must match the type of data in `target`.
```ts
const $user = createStore({});
const saveUserFx = createEffect((user: User) => {
// ...
});
sample({
clock: updateProfile,
source: $user,
fn: (user, updates) => ({ ...user, ...updates }),
target: saveUserFx,
});
sample({
clock: submit,
source: $form,
fn: (form) => form.email,
target: sendEmailFx,
});
```
***
#### `target`
The destination unit that will receive the data and be triggered.
Optional.
* **Type**
```ts
sample({
target?: Unit | Unit[],
})
```
Can be:
* EventCallable\ — a regular event (not derived) that will be called
* [`Effect`][effectApi] — an effect that will be triggered
* StoreWritable\ — a writable store that will be updated
* `Unit[]` — all units in the array will be called
> INFO target without target:
>
> If `target` is not specified, `sample` returns a new derived unit.
```ts
const targetEvent = createEvent