Перейти к основному содержимому

Store

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

Структура#

  • Методы

    • map: создает производный стор на основе данных из исходного
    • on: обновляет состояние стора с помощью функции-обработчика при срабатывании триггера
    • reset: сбрасывает состояние стора к исходному значению при срабатывании триггера
    • watch: вызывает функцию с сайд-эффектами при каждом обновлении стора
  • Свойства

    • updates: дочернее событие, представляющее обновления данного стора
    • defaultState: начальное состояние стора
    • shortName: имя стора
    • sid: стабильный идентификатор стора

Примеры#

Методы#

map#

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

Формула#

declare const $first: Store<T> // исходный стор
let $second: Store<S> // производный стор
$second = $first.map(/*fn*/ (state: T) => S)
$second = $first.map(/*fn*/ (state: T, lastState?: S) => S)

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

Аргументы#

  1. fn: (state: T, lastState?: S) => S

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

    Аргументы

    • state: Текущее состояние исходного стора $first на момент начала работы fn
    • lastState?: Текущее состояние производного стора $second на момент начала работы fn. Не существует при первом вызове fn

    Возвращает

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

Возвращает#

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

Примеры#

Пример использования map#
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#

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

Формула#

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. clock: Юнит или массив юнитов

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

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

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

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

    Аргументы

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

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

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

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

    Возвращает

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

Возвращает#

Текущий стор

note

Поддержка массивов в clock добавлена в 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

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

Использование массива юнитов в clock#
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

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

reset#

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

Формула#

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])

Аргументы#

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

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

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

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

Возвращает#

Текущий стор

note

Поддержка массивов в clock добавлена в 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()
// ничего не произошло (стор уже находится в исходном состоянии)

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

Использование массива юнитов в clock#
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()
// ничего не произошло (стор уже находится в исходном состоянии)

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

watch#

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

note

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

Формула#

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

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


Свойства#

updates#

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

note

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

Формула#

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

Примеры#

Логгирование с помощью shortName#
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

Примеры#

Пример использования defaultState#
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()

Примеры#

Пример использования 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

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

clock watch#

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

note

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

Формула#

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

Аргументы#

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

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

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

    Аргументы

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

Возвращает#

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

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

off#

Удаляет обработчик для данного триггера, который был установлен через .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)

Аргументы#

Возвращает#

Текущий стор

Примеры#

Пример использования off#
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#

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

Формула#

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

Аргументы#

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

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

Возвращает#

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

Примеры#

Пример использования thru#
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

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

Последнее обновление