Event API
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.”
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:
- Events, created using
createEvent
or.prepend
. These events are of typeEventCallable
and can be triggered directly or used in thetarget
argument of thesample
method. - Derived events, created using
.map
,.filter
, or.filterMap
. These are of typeEvent
and cannot be triggered or passed intotarget
— Effector triggers them internally in the correct order. However, you can subscribe to them viasample
orwatch
.
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)
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
const second = first.prepend(fn);
- Type
event.prepend<Before = void>(
fn: (_: Before) => Payload
): EventCallable<Before>
- Examples
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
You can treat this method as a wrapper function. Suppose you need to frequently call a function with an inconvenient API:
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:
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)
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
// Works for any event — both regular and derived
const first: Event<T> | EventCallable<T>;
const second: Event<F> = first.map(fn);
- Type
event.map<T>(fn: (payload: Payload) => T): Event<T>
- Examples
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]
- 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 })
sample
with the filter
argument is the preferred method for filtering:
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
const first: Event<T> | EventCallable<T>;
const second: Event<T> = first.filter({ fn });
- Type
event.filter(config: {
fn(payload: Payload): boolean
}): Event<Payload>
- Examples
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
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)
This method can also be replaced with a sample
operation using the filter
+ fn
arguments:
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
const first: Event<T> | EventCallable<T>;
const second: Event<F> = first.filterMap(fn);
- Type
event.filterMap<T>(fn: (payload: Payload) => T | undefined): Event<T>
- Examples
import { createEvent } from "effector";
const listReceived = createEvent<string[]>();
// 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
The function fn
must return some data. If undefined
is returned, the derived event call will be skipped.
- Return value
Returns a new derived event.
.watch(watcher)
The .watch
method calls the provided watcher
callback every time the event is triggered.
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
const event: Event<T> | EventCallable<T>;
const unwatch: () => void = event.watch(fn);
- Type
event.watch(watcher: (payload: Payload) => any): Subscription
- Examples
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
- 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:
You don’t need to use this method yourself. It’s used under the hood by rendering engines and so on.
- Formula
const event = createEvent();
event.subscribe(observer);
- Type
event.subscribe(observer: Observer<Payload>): Subscription
- Examples
import { createEvent } from "effector";
const userLoggedIn = createEvent<string>();
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
- Type
interface Event {
sid: string | null;
}
.shortName
Contains the variable name in which the event was declared.
import { createEvent } from "effector";
const demo = createEvent();
// demo.shortName === 'demo'
Reassigning the event to another variable doesn’t change this:
const another = demo;
// another.shortName === 'demo'
- Type
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.
Usually, if a long name is required, it’s better to pass it explicitly in the name
field.
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
interface Event {
compositeName: {
shortName: string;
fullName: string;
path: Array<string>;
};
}
Event Peculiarities
- 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. - 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 eventcreateApi
— create a set of events for a storemerge
— merge multiple events into onesample
— core operator to connect units
-
Articles