README
@wareset/store
Fast and easy observer. It is very similar to a 'store/writable' from Svelte and 'BehanivorSubject' from rxjs. But it has a slightly different mechanism for handling the subscription queue.
How to use:
Installation:
npm i @wareset/store ## yarn add @wareset/store
store(value: any, observed?: Array, depended?: Array, start?: Function)
import store from '@wareset/store';
const VAL$ = store(42);
The observer is a real regular array. The first element is reserved for storing the variable. This is how good compatibility is achieved.
Interesting moment:
Methods 'valueOf', 'toString' and 'toJSON' are forwarded from a stored variable. Therefore, the construction will work with primitives 'string' and 'number':
assert(typeof VAL$, 'object');
assert(VAL$ instanceof Array, true);
// but
assert(VAL$ + 12, 54); // 42 + 12
assert(store('Hello') + ' world!', 'Hello world!');
assert([...store(42)], [42]);
JSON.stringify(store(42)); // '42'
JSON.stringify(store({ q: 1 })); // '{ "q": 1 }'
Methods:
All methods are anonymous functions. Hence, they do not accept or change the context. All methods are synchronous!
get
get(), [0], $, value
assert(VAL$.get(), 42);
assert(VAL$[0], 42); // alias for 'get()' and 'set()'
assert(VAL$.$, 42); // alias for 'get()' and 'set()'
assert(VAL$.value, 42); // alias for 'get()' and 'set()'
set, subscribe and update:
set(newValue: any)
This method is similar to "set" from "svelte/store/writable" and "next" from "rxjs". But they always run subscribers if the value is an 'object'. This method doesn't do that. It always launches subscribers only if you pass a 'function'.
If you need to force subscribers to start, use 'setSure'. If you don't need to call the same function, use 'setWeak'.
VAL$.set(24);
assert(VAL$.get(), 24);
VAL$.next(12); // alias for 'set()'
assert(VAL$.get(), 12);
VAL$.$ = 25;
assert(VAL$.get(), 25);
VAL$[0] = 26;
assert(VAL$.get(), 26);
VAL$.value = 27;
assert(VAL$.get(), 27);
Subscriptions will be started if the value is 'different' from the previous one or if the value is a 'function'.
const VAL$ = store(42);
VAL$.$ = 42; // Nothing will happen
function func() {}
const VAL_2$ = store(func);
VAL2$.$ = func; // Will launch all subscriptions
setSure(newValue: any)
This will cause the subscriptions to run anyway. The rest of the methods 'Sure' behave exactly the same.
const VAL$ = store(42);
VAL$.setSure(42); // Will launch all subscriptions
setWeak(newValue: any, deep?: Number = 0)
This is a soft test. The rest of the methods 'Weak' behave exactly the same.
- deep - this is the depth of comparison for objects. See @wareset-utilites/deep-equal
const obj = { q: 1 };
const VAL$ = store(obj);
VAL$.setWeak(obj, 0); // Nothing will happen
VAL$.setWeak({ q: 1 }, 0); // Will launch all subscriptions
VAL$.setWeak({ q: 1 }, 1); // Nothing will happen
subscribe(callbackFn: (value, observed: Array, this: store, unsubscribe: Function), autostart: Boolean = true)
- autostart - Run it after creation
const VAL$ = store(42);
const unsubscribe = VAL$.subscribe((val, observed, this$, unsub) => {
assert(val, VAL$.$);
// observed - explained further in the text
assert(this$, VAL$);
assert(unsub, unsubscribe);
console.log('Value:', val);
return (val, observed, this$, unsub) => {
// unsub - it won't work anymore
// but if, on further calls, it will show the current value VAL$.$
console.log('Subscription canceled! Value: ', val);
};
});
VAL$.$ = VAL$.$ + 1; // 43
unsubscribe();
VAL$.$ = VAL$.$ + 1; // 44
unsubscribe();
console.log:
Value: 42
Value: 43
Subscription canceled! Value: 43
Subscription canceled! Value: 44 ## unsubscribed, but show the current value
Example of start and end functions:
const VAL$ = store(42, (val, observed, this$, unsub) => {
console.log('The first subscriber appeared!');
// unsub - this is unsubscribe for the first subscriber
// For example, let's cancel it.
unsub();
return (val, observed, this$, unsub) => {
// unsub - Backward compatibility stub () => {}
console.log('All subscribers have unsubscribed!');
};
});
VAL$.subscribe((val, observed, this$, unsub) => {
console.log('I am subscribed! Value:', val);
return (val, observed, this$, unsub) => {
// unsub - Backward compatibility stub () => {}
console.log('I was unsubscribed!');
};
});
console.log:
The first subscriber appeared!
I am subscribed! Value: 42!
I was unsubscribed!
All subscribers have unsubscribed!
update(callbackFn: (value, observed: Array, this: store, noop: Function) => newValue)
- noop - Backward compatibility stub
updateSure(callbackFn)
updateWeak(callbackFn, deep)
const VAL$ = store(42);
VAL$.subscribe(val => {
console.log('Subscriber 1. Value:', val);
});
VAL$.subscribe(val => {
console.log('Subscriber 2. Value:', val);
});
VAL$.update(val => {
return val + 12;
});
console.log:
Subscriber 1. Value: 42
Subscriber 2. Value: 42
Subscriber 1. Value: 54
Subscriber 2. Value: 54
observe and unobserve:
)
observe(stores: store | ArrayobserveSure(stores)
observeWeak(stores, deep)
These are obother observers whose updates affect subscriptions calls.
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(42);
VAL$.observe([SUBVAL_1$, SUBVAL_2$]);
// or const VAL = store(42, [SUBVAL_1, SUBVAL_2]);
// or VAL$.observe(SUBVAL_1$).observe(SUBVAL_2$);
// But, when observers are in an array - previous observers are erased:
// VAL$.observe([SUBVAL_1$]).observe([SUBVAL_2$]); - will unobserve SUBVAL_1$
SUBVAL_1$.subscribe(subval_1 => {
console.log('Subval_1: ', subval_1);
});
SUBVAL_2$.subscribe(subval_2 => {
console.log('Subval_2: ', subval_2);
});
VAL$.subscribe((val, [subval_1, subval_2]) => {
console.log('Value: ', val, 'Subvalues: ', [subval_1, subval_2]);
});
SUBVAL_1$.set(1);
SUBVAL_2$.set(1);
console.log:
Subval_1: 0
Subval_2: 0
Value: 42 Subvalues: [0, 0]
Value: 42 Subvalues: [1, 0]
Subval_1: 1
Value: 42 Subvalues: [1, 1]
Subval_2: 1
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(0, [SUBVAL_1$, SUBVAL_2$]);
VAL$.subscribe(val => {
console.log('Value: ', val);
});
SUBVAL_1$.subscribe(subval_1 => {
console.log('Subval_1: ', subval_1);
});
SUBVAL_2$.subscribe(subval_2 => {
console.log('Subval_2: ', subval_2);
});
VAL$.subscribe((val, [subval_1, subval_2]) => {
console.log('This subscriber will not be updated,');
console.log('because it is the source of the update itself');
if (subval_1 < 2) SUBVAL_1$.$ = 20;
if (subval_2 < 3) SUBVAL_2$.$ = 30;
VAL$.$ = SUBVAL_1$.$ + SUBVAL_2$.$;
});
console.log:
Value: 0
Subval_1: 0
Subval_2: 0
This subscriber will not be updated,
because it is the source of the update itself
Value: 50
Subval_2: 30
Subval_1: 20
)
unobserve(stores: store | ArrayRemoves the observer from the 'observed'.
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(42, [SUBVAL_1$, SUBVAL_2$]);
VAL$.unobserve(SUBVAL_2$);
VAL$.subscribe((val, [subval_1]) => {
// ...
});
observable and unobservable:
)
observable(stores: store | ArrayobservableSure(stores)
observableWeak(stores, deep)
Like 'observe', just the opposite.
const SUBVAL_1$ = store(0);
const VAL$ = store(42);
assert(VAL$.observe([SUBVAL_1$]), SUBVAL_1$.observable([VAL$]));
)
unobservable(stores: store | ArrayLike 'unobserve', just the opposite.
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(42, [SUBVAL_1$, SUBVAL_2$]);
SUBVAL_2$.unobservable(VAL$);
VAL$.subscribe((val, [subval_1]) => {
// ...
});
depend and undepend:
)
depend(stores: store | Arraydepend(stores)
depend(stores, deep)
These are other observers whose updates update the current observer.
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(0);
VAL$.depend([SUBVAL_1$, SUBVAL_2$]);
// or const VAL = store(42, [], [SUBVAL_1, SUBVAL_2]);
// or VAL$.depend(SUBVAL_1$).depend(SUBVAL_2$);
// But, when observers are in an array - previous observers are erased:
// VAL$.depend([SUBVAL_1$]).depend([SUBVAL_2$]); - will undeped SUBVAL_1$
SUBVAL_1$.subscribe(subval_1 => {
console.log('Subval_1: ', subval_1);
});
SUBVAL_2$.subscribe(subval_2 => {
console.log('Subval_2: ', subval_2);
});
VAL$.subscribe(val => {
console.log('Value: ', val);
});
SUBVAL_1$.set(1);
SUBVAL_2$.set(5);
console.log:
Subval_1: 0
Subval_2: 0
Value: 0
Subval_1: 1
Value: 1
Subval_2: 5
Value: 5
This is selectivity for depend stores:
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(0);
VAL$.depend([SUBVAL_1$, SUBVAL_2$]);
SUBVAL_1$.subscribe(subval_1 => {
console.log('Subval_1: ', subval_1);
});
SUBVAL_2$.subscribe(subval_2 => {
console.log('Subval_2: ', subval_2);
});
VAL$.subscribe(val => {
console.log('Value: ', val);
});
VAL$.subscribe(val => {
console.log('Value2: ', val);
VAL$.$ = SUBVAL_1$.$ + SUBVAL_2$.$;
if (SUBVAL_1$.$ < 2) ++SUBVAL_1$.$;
if (SUBVAL_2$.$ < 3) ++SUBVAL_2$.$;
VAL$.$ = SUBVAL_1$.$ + SUBVAL_2$.$;
if (SUBVAL_1$.$ < 2) ++SUBVAL_1$.$;
if (SUBVAL_2$.$ < 3) ++SUBVAL_2$.$;
VAL$.$ = SUBVAL_1$.$ + SUBVAL_2$.$;
if (SUBVAL_1$.$ < 2) ++SUBVAL_1$.$;
if (SUBVAL_2$.$ < 3) ++SUBVAL_2$.$;
VAL$.$ = SUBVAL_1$.$ + SUBVAL_2$.$;
});
console.log:
Subval_1: 0
Subval_2: 0
Value: 0
Value2: 0
Value: 5
Subval_2: 3
Subval_1: 2
Observers are updated in turn. And the final result will only be at the end.
)
undepend(stores: store | ArrayRemoves the observer from the 'depended'.
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(42, [], [SUBVAL_1$, SUBVAL_2$]);
VAL$.undepend(SUBVAL_2$);
dependency and undependency:
)
dependency(stores: store | ArraydependencySure(stores)
dependencyWeak(stores, deep)
Like 'depend', just the opposite.
const SUBVAL_1$ = store(0);
const VAL$ = store(42);
assert(VAL$.depend([SUBVAL_1$]), SUBVAL_1$.dependency([VAL$]));
)
undependency(stores: store | ArrayLike 'undepend', just the opposite.
const SUBVAL_1$ = store(0);
const SUBVAL_2$ = store(0);
const VAL$ = store(42, [], [SUBVAL_1$, SUBVAL_2$]);
SUBVAL_2$.undependency(VAL$);
bridge and unbridge:
)
bridge(stores: store | ArraybridgeSure(stores)
bridgeWeak(stores, deep)
Establishes interdependence between observers
const VAL_1$ = store(0);
const VAL_2$ = store(0);
const VAL_3$ = store(0);
VAL_1$.bridge([VAL_2$, VAL_3$]);
/*
this is like:
VAL_1$.depend(VAL_2$).depend(VAL_3$);
VAL_2$.depend(VAL_1$).depend(VAL_3$);
VAL_3$.depend(VAL_1$).depend(VAL_2$);
*/
// or VAL_2$.bridge([VAL_1$, VAL_3$]);
// or VAL_3$.bridge([VAL_1$, VAL_2$]);
VAL_1$.subscribe(val_1 => console.log('Val_1:', val_1));
VAL_2$.subscribe(val_2 => console.log('Val_2:', val_2));
VAL_3$.subscribe(val_3 => console.log('Val_3:', val_3));
++VAL_1$.$;
++VAL_2$.$;
++VAL_3$.$;
console.log:
Val_1: 0
Val_2: 0
Val_3: 0
Val_1: 1
Val_1: 1
Val_3: 1
Val_2: 2
Val_1: 2
Val_3: 2
Val_3: 3
Val_1: 3
Val_2: 3
)
unbridge(stores: store | ArrayRemoves the 'bridge'.
const VAL_1$ = store(0);
const VAL_2$ = store(0);
const VAL_3$ = store(0);
VAL_1$.bridge([VAL_2$, VAL_3$]);
VAL_1$.unbridge(VAL_3$);
// VAL_3$ no longer depends on and does not affect the VAL_1$ and VAL_2$
Lisence
MIT