import { sample } from "effector";

Methods

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

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

Quite a common case, when you need to handle an 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 the sample method.

Formulae

sample({ source?, clock?, filter?, 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 triggered on every source update.
  • If the filter is not passed, continue as it is. If filter return false or contains Store<false> cancel execution otherwise continue
  • 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()

Schema

Types

Type of the created target

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

clock\sourceStoreEventEffect
StoreStoreEventEvent
EventEventEventEvent
EffectEventEventEvent

How to read it:

  1. You need to know the 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:

import { sample } from "effector";

const $store = sample({ clock: $store, source: $store });
// Result will be store, because `source` and `clock` are stores.

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

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

Formulae

TBD

Arguments

params (Object): Configuration object

  • clock?: Unit or array of units
    • If event or effect: trigger target upon event or effect is called
    • If store: trigger target upon store is updated
    • If array of units: trigger target upon any given unit is called or updated. Shorthand for inline merge call
    • If not passed: source is used as clock
  • source?: Unit or object/array with stores
    • If event or effect: take last invocation argument value. That event or effect must be invoked at least once
    • If store: take current state of given store
    • If array or object with stores: take values from given stores combined to object or array. Shorthand for inline combine call
    • If not passed: clock is used as source
  • target?: Unit or array of units
    • If event or effect: call given event or effect upon clock is triggered
    • If store: update given store upon clock is triggered
    • If array of units: trigger every given unit upon clock is triggered
    • If not passed: new unit will be created under the hood and will be returned as a result of the sample() call. Type of created target is described in table above
  • filter? (Function or Store) ((sourceData, clockData) => result): boolean | Store<boolean>: If returns value of the function or store contains true continue execution otherwise cancel
  • fn? (Function) ((sourceData, clockData) => result): Combinator function, which will transform data from source and clock before passing it to target, should be pure. If not passed, data from source will be passed to target as it is
  • greedy? (boolean) 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
Deprecated

Since effector 23.0.0 property greedy is deprecated.

Use batch instead of greedy.

since

Array of units in target are supported since effector 21.8.0

Returns

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

Examples

import { createStore, createEvent, createEffect, sample } from "effector";

const submitForm = createEvent();
const signInFx = createEffect((params) => {
  console.log(params);
});

const $userName = createStore("john");

sample({
  clock: submitForm /* 1 */,
  source: $userName /* 2 */,
  fn: (name, password) => ({ name, password }) /* 3 */,
  target: signInFx /* 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. trigger effect signInFx 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.

Formulae

TBD

Arguments

  • sourceUnit: Source unit
    • If event or effect. Take last invocation argument value. That event or effect must be invoked at least once
    • If store. Take current storeโ€™s state
  • clockUnit: Clock unit. If not passed, source is used as clock
    • If event or effect. Trigger the sampled unit, upon event or effect is called
    • If store. Trigger the 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 triggered, if source is not passed. The type of returned unit depends on the types of clock and source.

Examples

import { createStore, createEvent, createEffect, sample } from "effector";

const submitForm = createEvent();

const signInFx = createEffect((params) => {
  console.log(params);
});

const $userName = createStore("john");

const sampleUnit = sample(
  $userName /* 2 */,
  submitForm /* 1 */,
  (name, password) => ({ name, password }) /* 3 */,
);
/* 4 */
sample({
  clock: sampleUnit,
  target: signInFx,
});

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 triggered,
//    send it payload to effect signInFx with params received at the step (3)

Try it

sample({name?})

Every unit in effector may have a name. You now can name sampled entities in the same manner as basic ones.

import { createStore, sample } from "effector";

const $store = createStore(null);

const sampled = sample({
  source: $store,
  name: "sampled $store",
});

console.log(sampled.shortName); // 'sampled foo'

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

Object of Stores

sample can be called with an 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({
  clock: trigger,
  source: { a: $a, b: $b },
});

target.watch((obj) => {
  console.log("sampled object", obj);
});

trigger();
// => sampled object {a: 'A', b: 1}

Try it

Array of Stores

sample can be called with an array of Store as source:

Note: Typescript requires adding as const after the array is entered.

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({
  clock: trigger,
  source: [$a, $b] as const,
});

target.watch((obj) => {
  console.log("sampled array", obj);
});

// You can easily destructure arguments to set explicit names
target.watch(([a, b]) => {
  console.log("explicit names", a, b);
});

trigger();
// => sampled array ["A", 1]
// => explicit names "A" 1

Try it

Array of Units in sample({ clock })

clock field in sample supports passing arrays of units, acting similarly to a merge call.

import {createStore, createEvent, createEffect, sample, merge} from 'effector'

const showNotification = createEvent<string>()
const trigger = createEvent()
const fx = createEffect()
const $store = createStore('')

// array of units in `clock`
sample({
  clock: [trigger, fx.doneData],
  source: $store,
  target: showNotification,
})

// merged unit in `clock`
sample({
  clock: merge([trigger, fx.doneData]),
  source: $store,
  target: showNotification,
})

Try it

Filtering updates with sample({ filter })

The new variant of the sample works the same but with one extra method filter. Whenever filter returns true continue execution otherwise cancel. Letโ€™s see an example below.

Henry wants to send money to William. Henry โ€“ sender and William โ€“ recipient. To send money, sender should know the recipient address, besides sender has to sign the transaction. This example shows how exactly the sample works with a filter. The main points are:

  1. Make sure balance is positive and more than sending amount
  2. Having recipient address
  3. Signed transaction
  4. Make sure sender balance has been changed
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);

Try it

Contributors