sample

This method can be used for linking two nodes, resulting the third one, which will fire only upon clock node trigger.

Quite a common case, when you need to handle some event with some store's state. Instead of using store.getState(), which may cause race conditions and inconsistency of state, it is more suitable to use sample method.

Formulae

sample({ source, clock?, fn?, target?}): target

When clock is triggered, read the value from source and trigger target with it.

  • If the clock is not passed, sample will be trigged on every target update.
  • If the fn is passed, pass value from source through before passing to target
  • If the target is not passed, create it and return from sample()

Type of the created target

If target is not passed to sample() call, it will be created internally. The type of the unit is described in the table below:

clock\sourceStoreEventEffect
StoreStoreEventEvent
EventEventEventEvent
EffectEventEventEvent

How to read it:

  1. You need to know type of the source, it is a column
  2. Type of the clock in the rows
  3. Match the column and the row

For example:

const $store = sample({ source: $store, clock: $store });
// Result will be store, because source and clock are stores.
const event = sample({ source: $store, clock: event });
// Because not all arguments are stores.

sample({source, clock?, fn?, target?, greedy?})

Arguments

params (Object): Configuration object

  • source (Event | Effect | Store): Source unit.
    • If event. Take last event invocation argument value. Event must be invoked at least once.
    • If effect. Take last effect invocation argument value. Effect must be invoked at least once.
    • If store. Take current store`s state.
  • clock? (Event | Effect | Store): Clock unit. If not passed, the source is used as clock.
    • If event. Triger sampled unit, upon event is called.
    • If effect. Triger sampled unit, upon effect is called.
    • If store. Triger sampled unit, upon store is updated.
  • fn? ((sourceData, clockData) => result): Optional combinator function, should be pure. Since, this handler is supposed to organize data flow, you should avoid declaring side-effects here. It's more appropriate to place it in watch method for sampled node.
  • target? (Event | Effect | Store): can contain Unit, which accepts payload returned by fn. In case if target is not passed, it's created "under the hood" and being returned as result of the sample() call.
  • greedy? (true | false) Modifier defines whether sampler will wait for resolving calculation result, and will batch all updates, resulting only one trigger, or will be triggered upon every linked node invocation, e.g. if greedy is true, sampler will fire on trigger of every node, linked to clock, whereas non-greedy sampler(greedy: false) will fire only upon the last linked node trigger.

Returns

(Event | Store) - Unit, which fires/updates upon clock is trigged, if source is not passed. The type of returned unit depends on the types of clock and source..

Example

const $userName = createStore('john')
const signIn = createEffect({handler: console.log})
const submitForm = createEvent()
sample({
source: $userName, /* 2 */
clock: submitForm, /* 1 */
fn: (name, password) => ({name, password}), /* 3 */
target: signIn, /* 4 */
})
submitForm(12345678)
// 1. when submitForm is called with params (12345678)
// 2. take $userName store`s state ('john')
// 3. transform payload from event (1) and current store`s state (2)
// 4. triger effect signIn with params received at the step (3)

Try it

sample(sourceUnit, clockUnit, fn?)

It is just another form of the sample invocation, with the same sense.

Arguments

  • sourceUnit (Event | Effect | Store): Source unit.
    • If event. Take last event invocation argument value. Event must be invoked at least once.
    • If effect. Take last effect invocation argument value. Effect must be invoked at least once.
    • If store. Take current store`s state.
  • clockUnit (Event | Effect | Store): Clock unit. If not passed, the source is used as clock.
    • If event. Triger sampled unit, upon event is called.
    • If effect. Triger sampled unit, upon effect is called.
    • If store. Triger sampled unit, upon store is updated.
  • fn? ((sourceData, clockData) => result): Optional combinator function, should be pure. Since, this handler is supposed to organize data flow, you should avoid declaring side-effects here. It's more appropriate to place it in watch method for sampled node.

Returns

(Event | Store) - Unit, which fires/updates upon clock is trigged, if source is not passed. The type of returned unit depends on the types of clock and source..

Example

const $userName = createStore('john')
const signIn = createEffect({handler: console.log})
const submitForm = createEvent()
const sampleUnit = sample(
$userName /* 2 */,
submitForm /* 1 */,
(name, password) => ({name, password}) /* 3 */
)
/* 5 */
forward({
from: sampleUnit,
to: signIn,
})
submitForm(12345678)
// 1. when submitForm is called with params (12345678)
// 2. take $userName store`s state ('john')
// 3. transform payload from event (1) and current store`s state (2)
// 4. when sampleUnit (event in this case) is trigered,
// send it payload to effect signIn with params received at the step (3)

Try it

Objects and arrays of Store in sample({ source })

Object of stores

sample can be called with object of Store as source:

import { createStore, createEvent, sample } from 'effector'
const trigger = createEvent()
const a = createStore('A')
const b = createStore(1)
// Target has type `Event<{ a: string, b: number }>`
const target = sample({
source: { a, b },
clock: trigger,
})
target.watch((obj) => {
console.log('sampled object', obj)
// => {a: 'A', b: 1}
})

Try it

Array of stores

sample can be called with array of Store as source:

import { createStore, createEvent, sample } from 'effector'
const trigger = createEvent()
const a = createStore('A')
const b = createStore(1)
// Target has type `Event<[string, number]>`
const target = sample({
source: [a, b],
clock: trigger,
})
target.watch((obj) => {
console.log('sampled array', obj)
// => ["A", 1]
})
// You can easily destructure arguments to set explicit names
target.watch(([a, b]) => {
console.log('Explicit names', a, b)
// => "A" 1
})

Try it