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.
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,
}));
// update array
$users.on(userAdded, (users, newUser) => {
users.push(newUser); // mutation!
return users;
});
// update object
$user.on(nameChanged, (user, newName) => {
user.name = newName; // mutation!
return user;
});
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[]>([]);
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:
- 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>
}
<script setup>
import { useUnit } from "effector-vue/composition";
import { $counter } from "./model.js";
const counter = useUnit($counter);
</script>
import { useUnit } from 'effector-solid'
import { $counter } from './model.js'
const Counter = () => {
const counter = useUnit($counter)
return <div>{counter()}</div>
}
- Subscribe to changes via watch - only for debug or integration needs
$counter.watch((counter) => {
console.log("Counter changed:", counter);
});
- 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,
});
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:
- Access to current values of all stores
- Atomic updates of multiple stores
- Convenient data transformation through the
fn
function - Ability to filter updates using
filter
- 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
import { createStore, createEvent } from "effector";
const $counter = createStore(0);
const incrementClicked = createEvent();
const decrementClicked = createEvent();
const resetClicked = createEvent();
$counter
.on(incrementClicked, (state) => state + 1)
.on(decrementClicked, (state) => state - 1)
.reset(resetClicked);
// 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,
},
}));
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]);
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.