Store (стор) – это объект, который хранит значение состояния, то есть какие-либо данные. Стор обновляется при получении значения, которое не равно (!==) текущему и undefined. Является юнитом



Методы

map(fn)

Создает производный стор на основе данных из исходного

Формула

declare const $first: Store<T>; // исходный стор

let $second: Store<S>; // производный стор

$second = $first.map(/*fn*/ (state: T) => S);

При обновлении исходного стора $first, функция-обработчик fn будет вызвана с новым состоянием $first и последним состоянием $second, результат вычислений будет сохранён в производном сторе $second, то есть реактивно обновит его значение

info

С версии effector 21.8.0 второй аргумент функции fn и firstState были депрекейтнуты, вместо этого используйте updateFilter или создание нового стора с помощью createStore.

Аргументы

  1. fn: (state: T) => S

    Функция-обработчик, которая будет вычислять новое состояние производного стора $second на основе значения исходного стора $first. Функция также генерирует и исходное состояние стора, поэтому в первый раз запускается в момент вызова .map, то есть ещё до создания производного стора. Должна быть чистой

    Аргументы

    • state: Текущее состояние исходного стора $first на момент начала работы fn

    Возвращает

    Новое значение для хранения в производном сторе $second. Если функция возвращает undefined или текущее состояние производного стора, то обновления не будет

Возвращает

Новый, производный стор

Пример

import { createEvent, createStore } from "effector";

const changed = createEvent();
const title = createStore("").on(changed, (_, newTitle) => newTitle);
const length = title.map((title) => title.length);

length.watch((length) => {
  console.log("длина строки", length);
});
// => длина строки 0

changed("hello");
// => длина строки 5

changed("world");
// ничего не произошло

changed("hello world");
// => длина строки 11

Запустить пример

on(trigger, reducer)

Обновляет состояние стора с помощью функции-обработчика при срабатывании триггера

Формула

declare const $store: Store<T>; // обновляемый стор

declare const event: Event<S>; // триггер обновления
declare const fx: Effect<S, any>; // триггер обновления
declare const $storeB: Store<S>; // триггер обновления

$store.on(/*clock*/ event, /*fn*/ (state: T, data: S) => T);
$store.on(/*clock*/ fx, /*fn*/ (state: T, data: S) => T);
$store.on(/*clock*/ $storeB, /*fn*/ (state: T, data: S) => T);
$store.on(/*clock*/ [event, fx, $storeB], /*fn*/ (state: T, data: S) => T);

Аргументы

  1. trigger: Юнит или массив юнитов

    Триггер начала вычисления или несколько триггеров. Для каждого триггера последний установленный обработчик будет заменять предыдущие обработчики (полезно для динамического поведения)

    Разновидности:

    • событие или эффект: срабатывание этого события/эффекта будет запускать обновление $store
    • стор: обновление этого стора будет запускать обновление $store
    • массив юнитов: срабатывание любого из юнитов будет запускать обновление $store
  2. reducer: (state: T, data: S) => T

    Функция-обработчик, которая будет вычислять новое состояние $store на основе его предыдущего состояния и данных из trigger, должна быть чистой

    Аргументы

    • state: Текущее состояние стора на момент начала работы fn

    • data: Данные, принятые от сработавшего trigger

      Разновидности, в зависимости от типа сработавшего trigger:

      • событие: значение, с которым было вызвано событие
      • эффект: значение, с которым был вызван эффект
      • стор: новое значение стора

    Возвращает

    Новое значение для хранения в $store. Если функция возвращает undefined или текущее состояние стора, то обновления не будет

Возвращает

Текущий стор

info

Поддержка массивов в trigger добавлена в effector 20.15.0

Пример

import { createEvent, createStore } from "effector";

const store = createStore(0);
const trigger = createEvent();

store.on(trigger, (state, data) => state + data);

store.watch((value) => {
  console.log(value);
});
// => 0

trigger(2);
// => 2

trigger(2);
// => 4

Запустить пример

Использование массива юнитов в trigger
import { createEvent, createStore } from "effector";

const store = createStore(0);
const triggerA = createEvent();
const triggerB = createEvent();

store.on([triggerA, triggerB], (state, data) => state + data);

store.watch((value) => {
  console.log(value);
});
// => 0

triggerA(2);
// => 2

triggerB(2);
// => 4

Запустить пример

watch(watcher)

Вызывает функцию с сайд-эффектами при каждом обновлении стора. В первый раз вызывается сразу же при создании, с текущим значением стора

info

По мере усложнения логики проекта оптимальнее заменить на комбинацию эффекта и sample

Формула

declare const $store: Store<T>

$store.watch(/*watcher*/ (state: T) => any)
-> Subscription

Аргументы

  • watcher: (state: T) => any

    Функция с сайд-эффектами, получает текущее состояние стора в качестве первого аргумента. Возвращаемое значение не используется

Возвращает

Subscription: Функция отмены подписки, после её вызова watcher перестаёт получать обновления и удаляется из памяти. Повторные вызовы функции отмены подписки не делают ничего

Пример

const add = createEvent();
const store = createStore(0).on(add, (state, payload) => state + payload);

store.watch((value) => {
  console.log(`текущее значение: ${value}`);
});
// => текущее значение: 0

add(4);
// => текущее значение: 4

add(3);
// => текущее значение: 7

Запустить пример

reset(...triggers)

Сбрасывает состояние стора к исходному значению при срабатывании триггера

Формула

declare const $store: Store<T>; // обновляемый стор

declare const event: Event<any>; // триггер обновления
declare const fx: Effect<any, any>; // триггер обновления
declare const $storeB: Store<any>; // триггер обновления

$store.reset(/*clock*/ event);
$store.reset(/*clock*/ fx);
$store.reset(/*clock*/ $storeB);
$store.reset(/*clock*/ [event, fx, $storeB]);
$store.reset(/*clock*/ ...[event, fx, $storeB]);

Аргументы

  • trigger: Юнит или массив юнитов

    Триггер запуска обновления стора или несколько триггеров. Если на момент срабатывания стор уже находится в исходном состоянии, то обновления не будет

    Разновидности:

    • событие или эффект: срабатывание этого события/эффекта будет запускать обновление $store
    • стор: обновление этого стора будет запускать обновление $store
    • массив юнитов: срабатывание любого из юнитов будет запускать обновление $store

Возвращает

Текущий стор

info

Поддержка массивов в trigger добавлена в effector 20.15.0

Пример

import { createEvent, createStore } from "effector";

const store = createStore(0);
const increment = createEvent();
const resetTrigger = createEvent();

store.on(increment, (state) => state + 1);
store.reset(resetTrigger);

store.watch((state) => {
  console.log(state);
});
// => 0

increment();
// => 1

increment();
// => 2

resetTrigger();
// => 0

resetTrigger();
// ничего не произошло (стор уже находится в исходном состоянии)

Запустить пример

Использование массива юнитов в trigger
import { createEvent, createStore } from "effector";

const store = createStore(0);
const increment = createEvent();
const triggerA = createEvent();
const triggerB = createEvent();

store.on(increment, (state) => state + 1);
store.reset([triggerA, triggerB]);

store.watch((state) => {
  console.log(state);
});
// => 0

increment();
// => 1

increment();
// => 2

triggerA();
// => 0

triggerB();
// ничего не произошло (стор уже находится в исходном состоянии)

Запустить пример

off(trigger)

Удаляет обработчик для данного триггера, который был установлен через .on или .reset. Если для данного триггера не было обработчика, этот метод ничего не делает

declare const $store: Store<any>; // обновляемый стор

declare const event: Event<any>; // триггер обновления
declare const fx: Effect<any, any>; // триггер обновления
declare const $storeB: Store<any>; // триггер обновления

$store.off(/*clock*/ event);
$store.off(/*clock*/ fx);
$store.off(/*clock*/ $storeB);

Аргументы

Возвращает

Текущий стор

Пример

import { createEvent, createStore } from "effector";

const click = createEvent();
const $clicks = createStore(0);

$clicks.on(click, (n) => n + 1);

$clicks.watch((n) => {
  console.log(n);
});
// => 0

click();
// => 1

$clicks.off(click);

click();
// ничего не произошло

Запустить пример

thru(fn)

Вызывает функцию с заданным стором и возвращает результат как есть. Используется для последовательных трансформаций заранее описанными функциями пока в javascript не добавлен pipeline proposal

Формула

declare const $store: Store<T>;

const result: S = $store.thru(/*fn*/ (store: Store<T>) => S);

Аргументы

  • fn: (store: Store<T>) => S

    Функция, которая получает сам стор в аргументы и возвращает некоторое значение, должна быть чистой

Возвращает

Любое значение, которое вернёт fn

Пример

import { createStore, createEvent } from "effector";

const enhance = (fn) => (store) => store.map(fn);

const inc = createEvent();
const $num = createStore(1);

$num.on(inc, (n) => n + 1);

//prettier-ignore
const $result = $num
  .thru(enhance(x => x + 1))
  .thru(enhance(x => x * 10))

$num.watch((n) => {
  console.log("num", n);
});
// => num 1

$result.watch((n) => {
  console.log("result", n);
});
// => result 20

inc();
// => num 2
// => result 30

Запустить пример

Свойства

updates

Дочернее событие стора, будет вызвано при каждом обновлении стора. Используется только для определения сайд-эффектов через store.updates.watch, где будут срабатывать начиная с первого обновления, в отличие от store.watch, обработчики в котором запускаются при создании немедленно

Вызов вручную запрещён

Это свойство управляется самим стором.

info

По мере усложнения логики проекта оптимальнее заменить на комбинацию эффекта и sample

Формула

declare const $store: Store<T>

$store.updates
-> Event<T>

Пример

Вызов сайд-эффектов начиная с первого обновления
import { createStore, createEvent } from "effector";

const click = createEvent();
const clicksAmount = createStore(0);

clicksAmount.on(click, (n) => n + 1);

clicksAmount.watch((amount) => {
  console.log("вызов с текущим состоянием, включая исходное", amount);
});

// => вызов с текущим состоянием, включая исходное 0

clicksAmount.updates.watch((amount) => {
  console.log("вызов с текущим состоянием, начиная с первого обновления", amount);
});

// ничего не произошло

click();
// => вызов с текущим состоянием, включая исходное 1
// => вызов с текущим состоянием, начиная с первого обновления 1

Запустить пример

shortName

Имя стора. Задаётся либо явно, через поле name в createStore, либо автоматически через Babel plugin. Используется для обработки сущностей программно, например при использовании хуков домена

Формула

declare const $store: Store<any>

$store.shortName
-> string

Пример

import { createDomain, createEvent } from "effector";

const increment = createEvent();

const storesDomain = createDomain();

storesDomain.onCreateStore((store) => {
  console.log(`создан стор '${store.shortName}'`);
  store.watch((value) => {
    console.log(`значение стора '${store.shortName}':`, value);
  });
});

const $foo = storesDomain.createStore(0, { name: "foo" });
// => создан стор 'foo'
// => значение стора 'foo': 0
const $bar = storesDomain.createStore(0, { name: "bar" });
// => создан стор 'bar'
// => значение стора 'bar': 0
$foo.on(increment, (n) => n + 1);

increment();
// => значение стора 'foo': 1

Запустить пример

defaultState

Начальное состояние стора, то, с которым он создавался. К этому состоянию будет возвращать метод reset

Формула

declare const $store: Store<T>

$store.defaultState
-> T

Пример

const $store = createStore("DEFAULT");

console.log($store.defaultState === "DEFAULT");
// => true

sid

Стабильный идентификатор стора. Задаётся автоматически через Babel plugin

Формула

declare const $store: Store<any>

$store.sid
-> string | null

Дополнительные методы

getState()

Возвращает текущее значение стора

Опасно использовать в своем коде

.getState порождает трудноотлаживаемый императивный код и состояния гонки данных

Оптимальнее использовать декларативные методы:

  • sample для использования данных из стора в других вычислениях
  • attach для передачи данных в эффекты

Формула

declare const $store: Store<T>;

const currentValue: T = $store.getState();

Пример

import { createEvent, createStore } from "effector";

const add = createEvent();

const $number = createStore(0);

$number.on(add, (state, data) => state + data);

$number.watch((n) => {
  console.log(n);
});
// => 0

add(2);
// => 2

add(3);
// => 5

Запустить пример

watch(trigger, watcher)

Сокращённая запись для описания сайд-эффекта, который необходимо запускать только при срабатывании определённого триггера и в котором необходимо как состояние стора так и данные из триггера

info

По мере усложнения логики проекта оптимальнее заменить на attach

Формула

declare const $store: Store<T>
declare const trigger: Event<S>

$store.watch(
  /*clock*/ trigger,
  /*fn*/ (state: T, data: S) => any,
)
-> Subscription

Аргументы

  1. trigger: Юнит-триггер

  2. fn: (state: T, data: S) => any

    Функция с сайд-эффектами. Возвращаемое значение не используется

    Аргументы

    • state: Текущее состояние стора на момент начала работы fn
    • data: Данные, принятые от сработавшего trigger

Возвращает

Subscription: Функция отмены подписки

Пример

import { createEvent, createStore } from "effector";

const foo = createEvent();
const bar = createEvent();

const store = createStore(0);

store.watch(foo, (storeValue, eventValue) => {
  console.log(`triggered ${storeValue}, ${eventValue}`);
});

foo(1);
// => triggered 0, 1

bar(2);

foo(3);
// => triggered 0, 3

Запустить пример

Перевод поддерживается сообществом

Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.

Помните, что переведенные статьи могут быть неактуальными, поэтому для получения наиболее точной и актуальной информации рекомендуем использовать оригинальную англоязычную версию документации.

Соавторы