Bu sahifa hali tarjima qilinmagan

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

Standart til uchun tarkibni ko'rsatadi.

State Management

State in effector is managed through stores - special objects that hold values and update them when receiving events. Stores are created using the createStore function.

Data immutability

Store data in effector is immutable, which means you should not mutate arrays or objects directly, but create new instances when updating them.

// update array
$users.on(userAdded, (users, newUser) => [...users, newUser]);

//update object
$user.on(nameChanged, (user, newName) => ({
  ...user,
  name: newName,
}));

Store Creation

import { createStore } from "effector";

// Create store with initial value
const $counter = createStore(0);
// with explicit typing
const $user = createStore<{ name: "Bob"; age: 25 } | null>(null);
const $posts = createStore<Post[]>([]);
Store naming

In effector it’s conventional to use the $ prefix for stores. This helps distinguish them from other entities and improves code readability.

Reading Values

There are several ways to get the current value of a store:

  1. Using framework integration hooks useUnit
import { useUnit } from 'effector-react'
import { $counter } from './model.js'

const Counter = () => {
  const counter = useUnit($counter)

  return <div>{counter}</div>
}
  1. Subscribe to changes via watch - only for debug or integration needs
$counter.watch((counter) => {
  console.log("Counter changed:", counter);
});
  1. getState() method - only for integration needs
console.log($counter.getState()); // 0

Store Updates

Updating via Events

The simplest and correct way to update a store is to bind it to an event:

import { createStore, createEvent } from "effector";

const incremented = createEvent();
const decremented = createEvent();
const resetCounter = createEvent();

const $counter = createStore(0)
  // Increase value by 1 each time the event is called
  .on(incremented, (counterValue) => counterValue + 1)
  // Decrease value by 1 each time the event is called
  .on(decremented, (counterValue) => counterValue - 1)
  // Reset value to 0
  .reset(resetCounter);

$counter.watch((counterValue) => console.log(counterValue));

// Usage
incremented();
incremented();
decremented();

resetCounter();

// Console output
// 0 - output on initialization
// 1
// 2
// 1
// 0 - reset

Updating with Event parameters

You can update a store using event parameters by passing data to the event like a regular function and using it in the handler.

import { createStore, createEvent } from "effector";

const userUpdated = createEvent<{ name: string }>();

const $user = createStore({ name: "Bob" });

$user.on(userUpdated, (user, changedUser) => ({
  ...user,
  ...changedUser,
}));

userUpdated({ name: "Alice" });

Complex Update Logic

Using the on method, we can update store state for simple cases when an event occurs, either by passing data from the event or updating based on the previous value.

However, this doesn’t always cover all needs. For more complex state update logic, we can use the sample method, which helps us when:

  • We need to control store updates using an event
  • We need to update a store based on values from other stores
  • We need data transformation before updating the store with access to current values of other stores

For example:

import { createEvent, createStore, sample } from "effector";

const updateItems = createEvent();

const $items = createStore([1, 2, 3]);
const $filteredItems = createStore([]);
const $filter = createStore("even");

// sample automatically provides access to current values
// of all connected stores at the moment the event triggers
sample({
  clock: updateItems,
  source: { items: $items, filter: $filter },
  fn: ({ items, filter }) => {
    if (filter === "even") {
      return items.filter((n) => n % 2 === 0);
    }

    return items.filter((n) => n % 2 === 1);
  },
  target: $filteredItems,
});
What is sample?

To learn more about what sample is, how to use this method, and its detailed description, you can read about it here.

Advantages of using sample for state updates:

  1. Access to current values of all stores
  2. Atomic updates of multiple stores
  3. Convenient data transformation through the fn function
  4. Ability to filter updates using filter
  5. Control over update timing through clock

Store Creation via restore method

If your store work involves replacing the old state with a new one when an event is called, you can use the restore method:

import { restore, createEvent } from "effector";

const nameChanged = createEvent<string>();

const $counter = restore(nameChanged, "");

The code above is equivalent to the code below:

import { createStore, createEvent } from "effector";

const nameChanged = createEvent<string>();

const $counter = createStore("").on(nameChanged, (_, newName) => newName);

You can also use restore method with an effect. In this case, the store will receive data from the effect’s doneData event, and the default store value should match the return value type:

import { restore, createEffect } from "effector";

type User = {
  id: number;
  name: string;
  age: number;
};

const createUserFx = createEffect<string, User>((id) => {
  // effect logic

  return {
    id: 4,
    name: "Bob",
    age: 18,
  };
});

const $newUser = restore(createEffect, {
  id: 0,
  name: "",
  age: -1,
});

createUserFx();

// $newUser = {
// 		id: 4,
// 		name: "Bob",
// 		age: 18,
//   }

Multiple Store Updates

A store isn’t limited to a single event subscription - you can subscribe to as many events as you need, and different stores can subscribe to the same event.

const categoryChanged = createEvent<string>();
const searchQueryChanged = createEvent<string>();
const filtersReset = createEvent();

const $lastUsedFilter = createStore<string | null>(null);
const $filters = createStore({
  category: "all",
  searchQuery: "",
});

// subscribe two different stores to the same event
$lastUsedFilter.on(categoryChanged, (_, category) => category);
$filters.on(categoryChanged, (filters, category) => ({
  ...filters,
  category,
}));

$filters.on(searchQueryChanged, (filters, searchQuery) => ({
  ...filters,
  searchQuery,
}));

$filters.reset(filtersReset);

In this example, we subscribe the $filters store to multiple events, and multiple stores to the same event categoryChanged.

Simplified Updates with createApi

When you need to create multiple handlers for one store, instead of creating separate events and subscribing to them, you can use createApi. This function creates a set of events for updating the store in one place.
The following code examples are equivalent:

import { createStore, createApi } from "effector";

const $counter = createStore(0);

const { increment, decrement, reset } = createApi($counter, {
  increment: (state) => state + 1,
  decrement: (state) => state - 1,
  reset: () => 0,
});

// usage
increment(); // 1
reset(); // 0

Derived Stores

Often you need to create a store whose value depends on other stores. For this, the map method is used:

import { createStore, combine } from "effector";

const $currentUser = createStore({
  id: 1,
  name: "Winnie Pooh",
});
const $users = createStore<User[]>([]);

// Filtered list
const $activeUsers = $users.map((users) => users.filter((user) => user.active));

// Computed value
const $totalUsersCount = $users.map((users) => users.length);
const $activeUsersCount = $activeUsers.map((users) => users.length);

// Combining multiple stores
const $friendsList = combine($users, $currentUser, (users, currentUser) =>
  users.filter((user) => user.friendIds.includes(currentUser.id)),
);

We also used the combine method here, which allows us to combine values from multiple stores into one.
You can also combine stores into an object:

import { combine } from "effector";

const $form = combine({
  name: $name,
  age: $age,
  city: $city,
});

// or with additional transformation
const $formValidation = combine($name, $age, (name, age) => ({
  isValid: name.length > 0 && age >= 18,
  errors: {
    name: name.length === 0 ? "Required" : null,
    age: age < 18 ? "Must be 18+" : null,
  },
}));
Important note

Derived stores update automatically when source stores change. You don’t need to manually synchronize their values.

Resetting State

You can reset store to default state via reset method:

const formSubmitted = createEvent();
const formReset = createEvent();

const $form = createStore({ email: "", password: "" })
  // Clear form on submit and on explicit reset too
  .reset(formSubmitted, formReset)
  // or
  .reset([formSubmitted, formReset]);

Full API reference for store

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