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 everysource
update. - If the
filter
is not passed, continue as it is. Iffilter
returnfalse
or containsStore<false>
cancel execution otherwise continue - If the
fn
is passed, pass value fromsource
through before passing totarget
- If the
target
is not passed, create it and return fromsample()
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\source | Store | Event | Effect |
---|---|---|---|
Store | Store | Event | Event |
Event | Event | Event | Event |
Effect | Event | Event | Event |
How to read it:
- You need to know the type of the
source
, it is a column - Type of the
clock
in the rows - 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 asclock
- If event or effect: trigger
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 assource
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
- If event or effect: call given event or effect upon
filter?
(Function or Store)((sourceData, clockData) => result): boolean | Store<boolean>
: If returns value of the function or store containstrue
continue execution otherwise cancelfn?
(Function)((sourceData, clockData) => result)
: Combinator function, which will transform data fromsource
andclock
before passing it totarget
, should be pure. If not passed, data fromsource
will be passed totarget
as it isgreedy?
(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. ifgreedy
istrue
,sampler
will fire on trigger of every node, linked toclock
, whereasnon-greedy sampler(greedy: false)
will fire only upon the last linked node trigger
Since effector 23.0.0 property greedy
is deprecated.
Use batch
instead of greedy
.
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)
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 asclock
- 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 inwatch
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)
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}
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
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,
})
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:
- Make sure balance is positive and more than sending amount
- Having recipient address
- Signed transaction
- 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);