Bu sahifa hali tarjima qilinmagan

Tarjima qoshish uchun havola boyicha o'tib Pull Request oching (havolaga o'tish).

Standart til uchun tarkibni ko'rsatadi.

Events

The Event in effector represents a user action, a step in the application process, a command to execute, or an intention to make modifications, among other things. This unit is designed to be a carrier of information/intention/state within the application, not the holder of a state.

In most situations, it is recommended to create events directly within the module, rather than placing them within conditional statements or classes, in order to maintain simplicity and readability. An exception to this recommendation is the use of factory functions; however, these should also be invoked at the root level of the module.

important information!

Event instances persist throughout the entire runtime of the application and inherently represent a portion of the business logic.

Attempting to delete instances and clear memory for the purpose of saving resources is not advised, as it may adversely impact the functionality and performance of the application.

Calling the event

There are two ways to trigger event: imperative and declarative.

The imperative method involves invoking the event as if it were a function:

import { createEvent } from "effector";

const callHappened = createEvent<void>();

callHappened(); // event triggered

The declarative approach utilizes the event as a target for operators, such as sample, or as an argument when passed into factory functions:

import { createEvent, sample } from "effector";

const firstTriggered = createEvent<void>();
const secondTriggered = createEvent<void>();

sample({
  clock: firstTriggered,
  target: secondTriggered,
});

When the firstTriggered event is invoked, the secondTriggered event will be subsequently called, creating a sequence of events. Remember, dont call events in pure functions, it’s not supported!

Good to know

In Effector, any event supports only a single argument. It is not possible to call an event with two or more arguments, as in someEvent(first, second).

All arguments beyond the first will be ignored. The core team has implemented this rule for specific reasons related to the design and functionality. This approach enables the argument to be accessed in any situation without adding types complexity.

If multiple arguments need to be passed, encapsulate them within an object:

import { createEvent } from "effector";

const requestReceived = createEvent<{ id: number; title: string }>();

requestReceived({ id: 1, title: "example" });

This rule also contributes to the clarity of each argument’s meaning, both at the call side and subscription side. It promotes clean and organized code, making it easier to understand and maintain.

Watching the event

To ascertain when an event is called, effector and its ecosystem offer various methods with distinct capabilities. Debugging is the primary use case for this purpose, and we highly recommend using patronum/debug to display when an event is triggered and the argument it carries.

import { createEvent, sample } from "effector";
import { debug } from "patronum";

const firstTriggered = createEvent<void>();
const secondTriggered = createEvent<void>();

sample({
  clock: firstTriggered,
  target: secondTriggered,
});

debug(firstTriggered, secondTriggered);

firstTriggered();
// => [event] firstTriggered undefined
// => [event] secondTriggered undefined

However, if your environment does not permit the addition of further dependencies, you may use the createWatch method, which accepts object in params with properties:

  • unit — unit or array of units, that you want to start watch
  • fn — function, that will be called when the unit is triggered. Accepts the unit’s payload as the first argument.
  • scopescope, instance of fork to restrict watcher calls on particular scope
import { createEvent, sample, createWatch } from "effector";

const firstTriggered = createEvent<void>();
const secondTriggered = createEvent<void>();

sample({
  clock: firstTriggered,
  target: secondTriggered,
});

const unwatch = createWatch({
  unit: [firstTriggered, secondTriggered],
  fn: (payload) => {
    console.log("[event] triggered");
  },
});

firstTriggered();
// => [event] triggered
// => [event] triggered
Keep in mind

The createWatch method neither handles nor reports exceptions, manages the completion of asynchronous operations, nor addresses data race issues.

Its primary intended use is for short-term debugging and logging purposes, or for tests to ensure that some unit was triggered.

Working with TypeScript

When an event is invoked, TypeScript will verify that the type of the argument passed matches the type defined in the event, ensuring consistency and type safety within the code.

This is also works for operators like sample or split:

import { sample, createEvent } from "effector";

const someHappened = createEvent<number>();
const anotherHappened = createEvent<string>();

sample({
  // @ts-expect-error error:
  // "clock should extend target type";
  // targets: { clockType: number; targetType: string; }
  clock: someHappened,
  target: anotherHappened,
});

Working with multiple events

Events in effector can be combined in various ways to create more complex logic. Let’s look at the main approaches:

Creating derived events

You can create a new event based on an existing one using the map method, which will be fired after original event:

import { createEvent, createStore } from "effector";

const userClicked = createEvent<{ id: number; name: string }>();
// Creating an event that will trigger only with the user's name
const userNameSelected = userClicked.map(({ name }) => name);
const $userName = createStore("").on(userNameSelected, (_, newName) => newName);

// Usage
userClicked({ id: 1, name: "John" });
// userNameSelected will get 'John'
Derived events

You cannot call derived events directly, but you can still subscribe to them for state changes or triggering other units.

Filtering events

If you wanna create a new event that triggers only when a certain condition is met, you can use sample method and filter param:

import { sample, createEvent } from "effector";

type User = { id: number; role: "admin" | "user" };
type Admin = { id: number; role: "admin" };

const userClicked = createEvent<User>();

// Event will trigger only for admins
const adminClicked = sample({
  clock: userClicked,
  filter: ({ role }) => role === "admin",
});

// Creating type-safe event
const typeSafeAdminClicked = sample({
  clock: userClicked,
  filter: (user): user is Admin => user.role === "admin",
});

Merging multiple events

You can use the merge method, which combines an array of units into a single event that will trigger when any of the array elements is called:

const buttonClicked = createEvent();
const linkClicked = createEvent();
const iconClicked = createEvent();

// Any of these events will trigger someActionHappened
const anyClicked = merge([buttonClicked, linkClicked, iconClicked]);

sample({
  clock: anyClicked,
  target: someActionHappened,
});

Or you can use sample with array in clock, which under the hood use the same method merge for arrays.

const buttonClicked = createEvent();
const linkClicked = createEvent();
const iconClicked = createEvent();

// Any of these events will trigger someActionHappened
sample({
  clock: [buttonClicked, linkClicked, iconClicked],
  target: someActionHappened,
});

Creating a pre-handler for an event

event.prepend is a method that creates a new event which will trigger the original event with preliminary data transformation.

Let’s say your application encounters different errors with different structures, but the error handling should happen centrally:

import { createEvent } from "effector";

// Main error handling event
const showError = createEvent<string>();

// Subscribe to error displays
sample({
  clock: showError,
  target: processErrorFx, // we'll skip the effect implementation
});

// Create special events for different types of errors
const showNetworkError = showError.prepend((code: number) => `Network error: ${code}`);

const showValidationError = showError.prepend(
  (field: string) => `Field ${field} is filled incorrectly`,
);

// Usage
showNetworkError(404); // 🔴 Error: Network error: 404
showValidationError("email"); // 🔴 Error: Field email is filled incorrectly

In this example:

  1. We have a main showError event that accepts a string
  2. Using prepend, we create two new events, each of which:
  • Accepts its own data type (number for network errors, string for validation errors)
  • Transforms this data into an error string
  • Passes the result to the main showError event

Conditional event triggering

The action chain when calling an event can trigger based on store states:

const buttonClicked = createEvent<void>();
const $isEnabled = createStore(true);

// Event will trigger only if $isEnabled is true
sample({
  clock: buttonClicked,
  filter: $isEnabled,
  target: actionExecuted,
});
Tip

Combining events through sample is preferred over directly calling events inside watch or other handlers, as it makes the data flow more explicit and predictable.

API reference for Event.

Tarjima jamiyat tomonidan qollanilyapti

Ingliz tilidagi hujjatlar eng dolzarb hisoblanadi, chunki u effector guruhi tomonidan yozilgan va yangilanadi. Hujjatlarni boshqa tillarga tarjima qilish jamiyat tomonidan kuch va istaklar mavjud bo'lganda amalga oshiriladi.

Esda tutingki, tarjima qilingan maqolalar yangilanmasligi mumkin, shuning uchun eng aniq va dolzarb ma'lumot uchun hujjatlarning asl inglizcha versiyasidan foydalanishni tavsiya etamiz.

Hammualliflar