Реактивность в effector
Реактивность — это фундаментальная концепция в Effector, которая позволяет автоматически обновлять данные при изменении их зависимостей. Вместо ручного управления обновлениями, вы описываете связи между различными частями вашего приложения, а Effector сам заботится об их синхронизации.
Что такое реактивность?
В основе реактивности лежит принцип автоматического распространения изменений. Когда меняется значение одного стора, все зависящие от него части приложения обновляются автоматически.
import { createStore, createEvent, combine } from "effector";
const firstWordChanged = createEvent<string>();
const $firstWord = createStore("Imperative");
const $secondWord = createStore("Programming");
const $fullSentence = combine($firstWord, $secondWord, (firstWord, secondWord) => {
return `${firstWord} ${secondWord}`;
});
const $fullSentenceLength = $fullSentence.map((fullSentence) => fullSentence.length);
// Связываем их
$firstWord.on(firstWordChanged, (_, newWord) => newWord);
// Вызов события приведет к обновлению стора и всех подписчиков
firstWordChanged("Reactive");
В этом примере при изменении состояния хранилища $firstWord
у нас автоматически изменится также $fullSentence
и $fullSentenceLength
, таким образом вам не нужно в ручную обновлять значения, этим займется effector.
Что делает Effector реактивным?
- Автоматическое распространение изменений. Когда значение стора меняется, Effector автоматически уведомляет все зависимые юниты:
import { createStore } from "effector";
const $users = createStore<User[]>([]);
const $userCount = $users.map((users) => users.length);
const $hasUsers = $users.map((users) => users.length > 0);
// $userCount и $hasUsers автоматически обновятся
// при любом изменении $users
- Декларативные связи. Вместо императивного описания что и когда должно произойти, мы декларативно описываем связи между данными:
import { sample, createStore, createEvent, createEffect } from "effector";
const formSubmitted = createEvent();
const $formData = createStore({ name: "", email: "" });
const submitToServerFx = createEffect(({ name, email }: { name: string; email: string }) => {
// logic
});
sample({
clock: formSubmitted,
source: $formData,
target: submitToServerFx,
});
- Предсказуемость обновлений. Обновления в Effector всегда происходят в определенном порядке, что делает поведение приложения предсказуемым:
import { createStore, createEvent, sample, createEffect } from "effector";
const $a = createStore(1);
const $b = createStore(2);
const updated = createEvent();
const updateFirstFx = createEffect(() => {
// logic
});
const updateSecondFx = createEffect(() => {
// logic
});
// Обновления будут происходить в порядке объявления
sample({
clock: updated,
source: $a,
target: updateFirstFx,
});
sample({
clock: updateFirstFx.done,
source: $b,
target: updateSecondFx,
});
Как работают связи между юнитами
Effector управляет зависимостями между юнитами (сторами, событиями и эффектами), обеспечивая правильный порядок обновлений. Когда одни юниты зависят от других, Effector гарантирует, что изменения распространяются предсказуемо:
import { createStore, createEvent, sample, createEffect } from "effector";
const fetchDataFx = createEffect(async () => {
// вызов api
});
// Создаем юниты
const buttonClicked = createEvent();
const $isLoading = createStore(false);
const $error = createStore<Error | null>(null);
// Создаем зависимости
$isLoading
.on(buttonClicked, () => true)
// Сброс состояния при завершении загрузки
.reset([fetchDataFx.done, fetchDataFx.fail]);
// При клике запускаем загрузку
sample({
clock: buttonClicked,
target: fetchDataFx,
});
buttonClicked();
Пример из реальной жизни
Рассмотрим пример поисковой строки с автоматическим обновлением результатов:
import { createStore, createEvent, createEffect, sample } from "effector";
// События и эффекты
const searchQueryChanged = createEvent<string>();
// 4
const searchFx = createEffect(async (query: string) => {
const response = await fetch(`/api/search?q=${query}`);
return response.json();
});
// Состояния
const $searchQuery = createStore("");
const $searchResults = createStore([]);
const $isSearching = searchFx.pending;
// Связи
$searchQuery.on(searchQueryChanged, (_, query) => query); // 2
// Обновляем результаты при успешном поиске
// 5
$searchResults.on(searchFx.doneData, (_, results) => results);
// Запускаем поиск при изменении запроса
// 3
sample({
clock: searchQueryChanged,
source: $searchQuery,
target: searchFx,
});
searchQueryChanged("qwerty"); // 1
- Где-то в приложении вызвали
searchQueryChanged
. - Обновляем стор
$searchQuery
. - При помощи
sample
мы декларативно вызываемtarget
(searchFx
) с данными изsource
. - Эффект
searchFx
выполняется. - В случае успешного эффекта
searchFx
мы обновляем данные в стореsearchResults
Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.
Помните, что переведенные статьи могут быть неактуальными, поэтому для получения наиболее точной и актуальной информации рекомендуем использовать оригинальную англоязычную версию документации.