☄️ Effector operators library delivering modularity and convenience
Table of contents
Predicate
- 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.
Effect
- Pending — Checks that has effects in pending state.
- InFlight — Counts all pending effects
- Status — Return text representation of effect state.
Time
- 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.
Combination/Decomposition
- 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
- Debug — Log triggers of passed units.
Usage
Please, review documentation for YOUR version of patronum not the latest. Find and open tag/release for your version.
npm install patronum
yarn add patronum
Import 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';
Create React App and Macros support
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.
Migration guide
v2.0.0
Removed support of effector v21. Now the minimum supported version is v22.1.2
.
v0.110
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
.
v0.100
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.
Condition
Method documentation & API
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');
trigger('welcome');
Try it
Delay
Method documentation & API
import { createEvent } from 'effector';
import { delay } from 'patronum/delay';
const trigger = createEvent<string>();
const delayed = delay({ source: trigger, timeout: 300 });
delayed.watch((payload) => console.info('triggered', payload));
trigger('hello');
Try it
Debounce
Method documentation & API
import { createEvent } from 'effector';
import { debounce } from 'patronum/debounce';
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);
Try it
Throttle
Method documentation & API
import { createEvent } from 'effector';
import { throttle } from 'patronum/throttle';
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);
Try it
Interval
Method documentation & API
import { 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);
Try it
Debug
Method documentation & API
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');
Try it
Status
Method documentation & API
import { 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}`));
effect();
Try it
Spread
Method documentation & API
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);
$second.watch(console.log);
Try it
Snapshot
Method documentation & API
import { 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');
createSnapshot();
Try it
CombineEvents
Method documentation & API
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);
event2('demo');
event3(5);
event1(true);
event2('demo');
reset();
event3(5);
event1(true);
event2('demo');
event3(5);
Try it
Every
Method documentation & API
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);
Try it
InFlight
Method documentation & API
import { 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);
Try it
Pending
Method documentation & API
import { 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}`));
loadFirst();
loadSecond();
Try it
Some
Method documentation & API
import { 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));
widthSet(200);
Try it
Reshape
Method documentation & API
import { 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);
parts.first.watch(console.log);
parts.second.watch(console.log);
Try it
SplitMap
Method documentation & API
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 });
serverActionReceived({ type: 'update', content: 'demo' });
serverActionReceived({ type: 'another' });
Try it
Time
Method documentation & API
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));
readTime();
Try it
Format
Method documentation & API
import { createStore } from 'effector';
import { format } from 'patronum';
const $firstName = createStore('John');
const $lastName = createStore('Doe');
const $fullName = format`${$firstName} ${$lastName}`;
$fullName.watch(console.log);
Try it
Reset
import { 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],
});
Try it
Development
You can review CONTRIBUTING.md
Release process
- 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"