☄️ Effector operators library delivering modularity and convenience
- Condition — Trigger then or else by condition.
- Some — Checks that state in at least one store passes the predicate test.
- Every — Checks that state in each store passes the predicate test.
- Reset — Reset all passed stores by clock.
- Pending — Checks that has effects in pending state.
- InFlight — Counts all pending effects
- Status — Return text representation of effect state.
- Debounce — Creates event which waits until time passes after previous trigger.
- Delay — Delays the call of the event by defined timeout.
- Throttle — Creates event which triggers at most once per timeout.
- Interval — Creates a dynamic interval with any timeout.
- Time — Allows reading current timestamp by triggering clock.
- CombineEvents — Wait for all passed events is triggered.
- Reshape — Destructure one store to different stores
- SplitMap — Split event to different events and map data.
- Spread — Send fields from object to same targets.
- Snapshot — Create store value snapshot.
- Format — Combine stores to a string literal.
- Debug — Log triggers of passed units.
Please, review documentation for YOUR version of patronum not the latest. Find and open tag/release for your version.
npm install patronum
# or
yarn add patronumImport function by its name from patronum:
import { delay } from 'patronum/delay';
import { inFlight } from 'patronum/in-flight';Also use can import it from index:
import { delay, inFlight } from 'patronum';Just import from patronum/macro, and imports will be replaced to full qualified:
import { status, splitMap, combineEvents } from 'patronum/macro';Warning: babel-plugin-macros do not support
import * as name!
Since release of patronum@2.0.0 it is required to use babel-plugin-macros@3.0.0 or higher.
Please note, that react-scripts@4.0.3 and older uses outdated version of this plugin - you can either use yarn resolutions or use react-scripts@5.0.0 or higher.
Removed support of effector v21. Now the minimum supported version is v22.1.2.
From v0.110.0 patronum removed support of effector v20. Now minimum supported version is v21.4.
Please, before upgrade review release notes of effector v21.
From v0.100.0 patronum introduced object arguments form with BREAKING CHANGES. Please, review migration guide before upgrade from v0.14.x on your project.
import { createEvent } from 'effector';
import { condition } from 'patronum/condition';
const trigger = createEvent<string>();
const longString = createEvent<string>();
const shortString = createEvent<string>();
condition({
source: trigger,
if: (string) => string.length > 6,
then: longString,
else: shortString,
});
longString.watch((str) => console.log('long', str));
shortString.watch((str) => console.log('short', str));
trigger('hi'); // => short hi
trigger('welcome'); // => long welcomeimport { createEvent } from 'effector';
import { delay } from 'patronum/delay';
const trigger = createEvent<string>(); // createStore or createEffect
// `timeout` also supports (payload) => number and Store<number>
const delayed = delay({ source: trigger, timeout: 300 });
delayed.watch((payload) => console.info('triggered', payload));
trigger('hello');
// after 300ms
// => triggered helloimport { createEvent } from 'effector';
import { debounce } from 'patronum/debounce';
// You should call this event
const trigger = createEvent<number>();
const target = debounce({ source: trigger, timeout: 200 });
target.watch((payload) => console.info('debounced', payload));
trigger(1);
trigger(2);
trigger(3);
trigger(4);
// after 200ms
// => debounced 4import { createEvent } from 'effector';
import { throttle } from 'patronum/throttle';
// You should call this event
const trigger = createEvent<number>();
const target = throttle({ source: trigger, timeout: 200 });
target.watch((payload) => console.info('throttled', payload));
trigger(1);
trigger(2);
trigger(3);
trigger(4);
// 200ms after trigger(1)
// => throttled 4import { createStore, createEvent } from 'effector';
import { interval } from 'patronum';
const startCounter = createEvent();
const stopCounter = createEvent();
const $counter = createStore(0);
const { tick } = interval({
timeout: 500,
start: startCounter,
stop: stopCounter,
});
$counter.on(tick, (number) => number + 1);
$counter.watch((value) => console.log('COUNTER', value));
startCounter();
setTimeout(() => stopCounter(), 5000);import { createStore, createEvent, createEffect } from 'effector';
import { debug } from 'patronum/debug';
const event = createEvent();
const effect = createEffect().use((payload) => Promise.resolve('result' + payload));
const $store = createStore(0)
.on(event, (state, value) => state + value)
.on(effect.done, (state) => state * 10);
debug($store, event, effect);
event(5);
effect('demo');
// => [store] $store 1
// => [event] event 5
// => [store] $store 6
// => [effect] effect demo
// => [effect] effect.done {"params":"demo", "result": "resultdemo"}
// => [store] $store 60import { createEvent, createEffect } from 'effector';
import { status } from 'patronum/status';
const effect = createEffect().use(() => Promise.resolve(null));
const $status = status({ effect });
$status.watch((value) => console.log(`status: ${value}`));
// => status: "initial"
effect();
// => status: "pending"
// => status: "done"import { createEvent, createStore } from 'effector';
import { spread } from 'patronum/spread';
const trigger = createEvent<{ first: string; second: string }>();
const $first = createStore('');
const $second = createStore('');
spread({
source: trigger,
targets: {
first: $first,
second: $second,
},
});
trigger({ first: 'Hello', second: 'World' });
$first.watch(console.log); // => Hello
$second.watch(console.log); // => Worldimport { restore, createEvent } from 'effector';
import { snapshot } from 'patronum/snapshot';
const changeText = createEvent<string>();
const createSnapshot = createEvent();
const $original = restore(changeText, 'Example');
const $snapshot = snapshot({
source: $original,
clock: createSnapshot,
});
changeText('New text');
// $original -> Store with "New text"
// $snapshot -> Store with "Example"
createSnapshot();
// $original -> Store with "New text"
// $snapshot -> Store with "New text"Call target event when all event from object/array is triggered
import { createEvent } from 'effector';
import { combineEvents } from 'patronum/combine-events';
const event1 = createEvent();
const event2 = createEvent();
const event3 = createEvent();
const reset = createEvent();
const event = combineEvents({
reset,
events: {
event1,
event2,
event3,
},
});
event.watch((object) => console.log('triggered', object));
event1(true); // nothing
event2('demo'); // nothing
event3(5); // => triggered { event1: true, event2: "demo", event3: 5 }
event1(true); // nothing
event2('demo'); // nothing
reset();
event3(5); // nothing
event1(true); // nothing
event2('demo'); // nothing
event3(5); // => triggered { event1: true, event2: "demo", event3: 5 }import { createStore } from 'effector';
import { every } from 'patronum/every';
const $isPasswordCorrect = createStore(true);
const $isEmailCorrect = createStore(true);
const $isFormCorrect = every([$isPasswordCorrect, $isEmailCorrect], true);
$isFormCorrect.watch(console.log); // => trueimport { createEffect } from 'effector';
import { inFlight } from 'patronum/in-flight';
const firstFx = createEffect().use(() => Promise.resolve(1));
const secondFx = createEffect().use(() => Promise.resolve(2));
const $allInFlight = inFlight({ effects: [firstFx, secondFx] });
firstFx();
secondFx();
firstFx();
$allInFlight.watch(console.log);
// => 3
// => 2
// => 1
// => 0import { createEffect } from 'effector';
import { pending } from 'patronum/pending';
const loadFirst = createEffect().use(() => Promise.resolve(null));
const loadSecond = createEffect().use(() => Promise.resolve(2));
const $processing = pending({ effects: [loadFirst, loadSecond] });
$processing.watch((processing) => console.info(`processing: ${processing}`));
// => processing: false
loadFirst();
loadSecond();
// => processing: true
// => processing: falseimport { createStore, restore, createEvent } from 'effector';
import { some } from 'patronum/some';
const widthSet = createEvent<number>();
const $width = restore(widthSet, 820);
const $height = createStore(620);
const $tooBig = some({
predicate: (size) => size > 800,
stores: [$width, $height],
});
$tooBig.watch((big) => console.log('big', big)); // => big true
widthSet(200);
// => big falseimport { createStore } from 'effector';
import { reshape } from 'patronum/reshape';
const $original = createStore<string>('Hello world');
const parts = reshape({
source: $original,
shape: {
length: (string) => string.length,
first: (string) => string.split(' ')[0] || '',
second: (string) => string.split(' ')[1] || '',
},
});
parts.length.watch(console.info); // 11
parts.first.watch(console.log); // "Hello"
parts.second.watch(console.log); // "world"import { createEvent } from 'effector';
import { splitMap } from 'patronum/split-map';
type Action =
| { type: 'update'; content: string }
| { type: 'created'; value: number }
| { type: 'another' };
const serverActionReceived = createEvent<Action>();
const received = splitMap({
source: serverActionReceived,
cases: {
update: (action) => (action.type === 'update' ? action.content : undefined),
created: (action) => (action.type === 'created' ? action.value : undefined),
},
});
received.update.watch((payload) =>
console.info('update received with content:', payload),
);
received.created.watch((payload) => console.info('created with value:', payload));
received.__.watch((payload) => console.info('unknown action received:', payload));
serverActionReceived({ type: 'created', value: 1 });
// => created with value: 1
serverActionReceived({ type: 'update', content: 'demo' });
// => update received with content: "demo"
serverActionReceived({ type: 'another' });
// => unknown action received: { type: "another" }import { createEvent } from 'effector';
import { time } from 'patronum/time';
const readTime = createEvent();
const $now = time({ clock: readTime });
$now.watch((now) => console.log('Now is:', now));
// => Now is: 1636914286675
readTime();
// => Now is: 1636914300691import { createStore } from 'effector';
import { format } from 'patronum';
const $firstName = createStore('John');
const $lastName = createStore('Doe');
const $fullName = format`${$firstName} ${$lastName}`;
$fullName.watch(console.log);
// => John Doeimport { createEvent, createStore } from 'effector';
import { reset } from 'patronum/reset';
const pageUnmounted = createEvent();
const userSessionFinished = createEvent();
const $post = createStore(null);
const $comments = createStore([]);
const $draftComment = createStore('');
reset({
clock: [pageUnmounted, userSessionFinished],
target: [$post, $comments, $draftComment],
});You can review CONTRIBUTING.md
- Check out the draft release.
- All PRs should have correct labels and useful titles. You can review available labels here.
- Update labels for PRs and titles, next manually run the release drafter action to regenerate the draft release.
- Review the new version and press "Publish"
- If required check "Create discussion for this release"