sample
Метод для связывания юнитов связью вида "при срабатывании clock прочитать значение из source и передать в target"
Типичный вариант использования - когда необходимо обработать какое-либо событие используя данные из стора. Вместо использования store.getState(), которое может вызвать несогласованность состояния, лучше использовать метод sample
Формула
sample({ source?, clock?, filter?, fn?, target?}): target
При срабатывании clock прочитать значение из source и передать в target
- Если
clockне передан, sample будет срабатывать при каждом обновленииsource. - Если
filterне передан, продолжить выполнение как есть. Еслиfilterесть и возвращаетfalseили его значениеStore<false>, то отменить выполнение, а иначе продолжить - Если передан
fn, то при срабатывании передать значения изsourceиclockв эту функцию, а вtargetпередать результат вычисления - Если
targetне передан, то sample создаст и вернёт новый юнит
Иллюстрация принципа работы

Тип создаваемого target
Если target не передан, то он будет создан при вызове. Тип создаваемого юнита описан в данной таблице:
| clock\source | Store | Event | Effect |
|---|---|---|---|
| Store | Store | Event | Event |
| Event | Event | Event | Event |
| Effect | Event | Event | Event |
Использование таблицы:
- Выбираем тип источника
source, это столбец - Тип
clock- это строка - Устанавливаем соответствие между столбцом и строкой
Например:
const $store = sample({clock: $store, source: $store})
// Результатом будет стор, так как source и clock являются сторами
const event = sample({clock: event, source: $store})
// Результатом будет эвент, так как clock - не стор
sample({clock?, source?, fn?, target?, greedy?})
Основная запись метода
Аргументы
params (Object): Объект конфигурации
clock?: Юнит или массив юнитовРазновидности:
- событие или эффект: срабатывание этого события/эффекта будет запускать
target - стор: обновление этого стора будет запускать
target - массив юнитов: срабатывание любого из юнитов будет запускать
target. Сокращение для вызова merge - поле отсутствует:
sourceбудет использоваться в качествеclock
- событие или эффект: срабатывание этого события/эффекта будет запускать
source?: Юнит или массив/объект со сторамиРазновидности:
- событие или эффект: при срабатывании
clockбудет взято последнее значение с которым запускался этот юнит (перед этим он должен будет запуститься хотя бы раз) - стор: при срабатывании
clockбудет взято текущее значение этого стора - массив или объект со сторами: при срабатывании
clockбудут взяты текущие значения из заданных сторов, объединенных в объект или массив. Сокращение для вызова combine - поле отсутствует:
clockбудет использоваться в качествеsource
- событие или эффект: при срабатывании
target?: Юнит или массив юнитовРазновидности:
- событие или эффект: при срабатывании
clockбудет вызван данный юнит - стор: при срабатывании
clockсостояние юнита будет обновлено - массив юнитов: при срабатывании
clockбудут запущены все юниты - поле отсутствует: новый юнит будет создан и возвращен в результате вызова sample. Его тип зависит от типов
clockиsource
- событие или эффект: при срабатывании
fn?:(sourceData, clockData) => resultФункция-обработчик, которая будет преобразовывать данные из
sourceиclockперед отправлением вtarget, должна быть чистой. В случае отсутствия этого поля, данные изsourceбудут передаваться вtargetкак естьgreedy?:booleanМодификатор, определяющий, будет ли
targetожидать окончательного значенияclockпрежде чем запуститься самому. Приgreedy: falsetargetбудет срабатывать только раз после каждой серии идущих подряд обновлений, а приgreedy: true,targetсработает по разу при каждом триггереclock. Иными словами, эта опция отключает стабилизацию апдейтовclockи вынуждает обрабатывать все промежуточные значения. Батчинг обновлений повышает общую эффективность работы системы, поэтому по умолчанию greedy установлен вfalse
Поддержка массивов юнитов в target добавлена в effector 21.8.0
Возвращает
(Event | Store) - Юнит, который будет срабатывать при срабатывании clock, если target не передан.
Тип возвращаемого юнита зависит от типов clock и source
Пример
const $userName = createStore('john')
const signIn = createEffect((params) => {
console.log(params)
})
const submitForm = createEvent()
sample({
clock: submitForm /* 1 */,
source: $userName /* 2 */,
fn: (name, password) => ({name, password}) /* 3 */,
target: signIn /* 4 */,
})
submitForm(12345678)
// 1. при вызове submitForm с аргументом 12345678
// 2. прочитать значение из стора $userName ('john')
// 3. преобразовать значение из submitForm (1) и $userName (2)
// 4. и передать результат вычислений в эффект signIn
sample(source, clock, fn?): Unit
Альтернативная запись метода, всегда имеет неявный target
Аргументы
source: ЮнитРазновидности:
- событие или эффект: при срабатывании
clockбудет взято последнее значение с которым запускался этот юнит (перед этим он должен будет запуститься хотя бы раз) - стор: при срабатывании
clockбудет взято текущее значение этого стора
- событие или эффект: при срабатывании
clock: ЮнитРазновидности:
- событие или эффект: срабатывание этого события/эффекта будет запускать
target - стор: обновление этого стора будет запускать
target - поле отсутствует:
sourceбудет использоваться в качествеclock
- событие или эффект: срабатывание этого события/эффекта будет запускать
fn?:(sourceData, clockData) => resultФункция-обработчик, которая будет преобразовывать данные из
sourceиclockперед отправлением вtarget, должна быть чистой. В случае отсутствия этого поля, данные изsourceбудут передаваться вtargetкак есть. Поскольку этот обработчик призван организовывать поток данных, следует избегать объявления в нём сайд-эффектов. Правильнее будет поместить их в эффекты или в методwatchвозвращаемого юнита
Возвращает
(Event | Store) - Юнит, который будет срабатывать при срабатывании clock, если target не передан.
Тип возвращаемого юнита зависит от типов clock и source.
Пример
const $userName = createStore('john')
const signIn = createEffect((params) => {
console.log(params)
})
const submitForm = createEvent()
const sampleUnit = sample(
$userName /* 2 */,
submitForm /* 1 */,
(name, password) => ({name, password}) /* 3 */,
)
forward({
from: sampleUnit,
to: signIn /* 4 */,
})
submitForm(12345678)
// 1. при вызове submitForm с аргументом 12345678
// 2. прочитать значение из стора $userName ('john')
// 3. преобразовать значение из submitForm (1) и $userName (2)
// 4. и передать результат вычислений в эффект signIn
sample({name?})
Добавлено в effector 20.4.0
Любой юнит в эффекторе может иметь имя, поле name в sample позволяет указать имя создаваемому target
import {createStore, sample} from 'effector'
const foo = createStore(null)
const sampled = sample({
source: foo,
name: 'sampled foo',
})
console.log(sampled.shortName) // 'sampled foo'
Объекты и массивы в source
Объект со сторами
Добавлено в effector 20.8.0
sample может быть вызван с объектом сторов в source:
import {createStore, createEvent, sample} from 'effector'
const trigger = createEvent()
const a = createStore('A')
const b = createStore(1)
// target имеет тип `Event<{ a: string, b: number }>`
const target = sample({
clock: trigger,
source: {a, b},
})
target.watch((obj) => {
console.log('sampled object', obj)
})
trigger()
// => sampled object {a: 'A', b: 1}
Массив сторов
Добавлено в effector 20.8.0
sample может быть вызван с массивом сторов в source:
import {createStore, createEvent, sample} from 'effector'
const trigger = createEvent()
const a = createStore('A')
const b = createStore(1)
// target имеет тип `Event<[string, number]>`
const target = sample({
clock: trigger,
source: [a, b],
})
target.watch((obj) => {
console.log('sampled array', obj)
})
// Можно деструктурировать аргументы, чтобы задать явные имена
target.watch(([a, b]) => {
console.log('explicit names', a, b)
})
trigger()
// => sampled array ["A", 1]
// => explicit names "A" 1
Массивы юнитов в clock
Добавлено в effector 21.2.0
Передача массивов юнитов в clock работает как вызов merge
import {createStore, createEvent, createEffect, sample, merge} from 'effector'
const showNotification = createEvent<string>()
const trigger = createEvent()
const fx = createEffect()
const store = createStore('')
// массив юнитов в clock
sample({
clock: [trigger, fx.doneData],
source: store,
target: showNotification,
})
// объединённый юнит в clock
sample({
clock: merge([trigger, fx.doneData]),
source: store,
target: showNotification,
})
Пример с filter
effector Halley 22.2.0
Новый вариант использования sample работает так же, но с одним дополнительным методом filter. Когда filter возвращает true продолжить выполнение, иначе отменить. Взглянем на пример ниже.
Вася хочет отправить Пете деньги. Вася - отправитель, а Петя - получатель. Чтобы отправить деньги, отправитель должен знать адрес получателя, кроме того транзакция должна быть подписана. Пример показывает как работает sample с filter. Основные моменты, которые необходимо учесть:
- Убедиться, что баланс положительный и больше чем отправляемая сумма.
- Наличие адреса получателя
- Подписанная транзакция
- Убедиться, что баланс отправителя изменился
import {createStore, createEvent, createEffect, sample} from 'effector'
const sign = createEvent()
const sentMoney = createEvent()
const $recipientAddress = createStore('a23x3xd')
const $balance = createStore(20000)
const $isSigned = createStore(false)
const transactionFx = createEffect(
({amountToSend, recipientAddress}) =>
new Promise((res) =>
setTimeout(res, 3000, {
amount: amountToSend,
recipientAddress,
}),
),
)
$isSigned.on(sign, () => true).reset(transactionFx)
$balance.on(transactionFx.doneData, (balance, {amount}) => balance - amount)
sample({
source: {
recipientAddress: $recipientAddress,
isSigned: $isSigned,
balance: $balance,
},
clock: sentMoney,
filter: ({isSigned, balance}, amountToSend) =>
isSigned && balance > amountToSend,
fn({recipientAddress}, amountToSend) {
return {recipientAddress, amountToSend}
},
target: transactionFx,
})
$balance.watch((balance) => console.log('balance: ', balance))
$isSigned.watch((isSigned) => console.log('is signed: ', isSigned))
sign()
sentMoney(1000)