rxjs-lib
v1.0.5
Published
RX Utils Library - helpers for managing state, based on RxJS
Downloads
5
Readme
RXJS Util
Fully inspired by Mark E's @rx-utils
State management utilities based on RxJS
This library provides a very thin wrapper around some RxJS objects that are useful for state management.
There are only two main components: atom
and combine
.
Install
// Via NPM
$ npm install --save rxjs-lib
// Via Yarn
$ yarn add rxjs-lib
Usage
atom
This is very simply a container for any value that changes. It's a thin wrapper around an RxJS BehaviorSubject
.
You simply wrap a static value with atom
import { atom } from "rxjs-lib";
const word$ = atom("Hello");
The $
at the end is a convention sometimes used to indicate an observable object.
Now you can subscribe to updates with a callback
const sub = word$.subscribe((word) => console.log(`Word is now ${word}`));
// Logs "Word is now Hello"
Set a new value
word$.set("Hello World");
// Logs "Word is now Hello World"
To avoid memory leaks you should unsubscribe when finished
sub.unsubscribe();
The atom
function returns a WritableAtom
, which has the following methods:
const $word = atom("Hello");
$word.get() // "Hello"
$word.set("Hey") // sets value to "Hey" and notifies subscribers
$word.update(n => n + " World") // set to "Hello World" -
// use instead of set if you want to use the previous value
const uppercaseWord$ = $word.pipe( // pipe can be used as per usual in RxJS,
map(n => n.toUpperCase()), // and returns an RxJS observable
...
)
const lowercaseWord$ = $word.map(n => n.toLowerCase()) // "map" is provided for convenience so you
// don't need pipe, and returns a ReadonlyAtom
const $wordRO = $word.readonly() // returns a read-only version of the atom (ReadonlyAtom)
$wordRO.get() // "Hello World"
$wordRO.set(...) // ERROR - METHOD DOESN'T EXIST!
$word.destroy() // rarely used but can use to remove all subscribers
The ReadonlyAtom
is similar but only has get
, map
, pipe
and subscribe
.
readonlyAtom
This is a convenience method for creating a read-only atom, that also yields a setter function.
const [count$, setCount] = readonlyAtom(4);
count$.get(); // 4 - this is just a ReadonlyAtom
setCount(7);
count$.get(); // 7
This would be useful for e.g. using in a class, where the read-only atom is public, but the setter is private:
class Person {
public name$: ReadonlyAtom<string>;
private setName: (name: string) => void;
constructor(initialName: string) {
[this.name$, this.setName] = readonlyAtom(initialName); // NOTE the parentheses when doing this
}
//... use this.setName("...") internally
}
const person = new Person("Fred");
person.name$.set("Bubba"); // ERROR: name$ is readonly so has no 'set'
combine
This combines RxJS observables or atoms in a way that is useful for efficiently using derived values.
Given multiple atoms (or other synchronous RxJS observables)
const names$ = atom(["Geoffrey", "Bungle", "George", "Zippy"]);
const selectedIndex$ = atom(1);
Then you can combine them into a new observable with a tuple
const selectedName$ = combine(
[names$, selectedIndex$], // tuple of multiple observables
([names, index]) => names[index] // calculate new value derived from values from observables
);
selectedName$.get(); // "Bungle"
or with an object
const selectedName$ = combine(
{ names: names$, idx: selectedIndex$ }, // object lookup of multiple observables
({ names, idx }) => names[idx] // calculate derived value
);
selectedName$.get(); // "Bungle"
The new observable is efficient in that
- it only makes the calculation (the 2nd argument function) once when any of its input observables have changed
- it doesn't make the calculation if no-one is subscribing
In both forms you can also call combine
with no 2nd argument
const selectedName$ = combine({ names: names$, idx: selectedIndex$ }); // = an observable that emits {names: string[], idx: number} objects
const selectedName$ = combine([names$, selectedIndex$]); // = an observable that emits [string[], number] objects
get
This library provides a convenience method for synchronously getting the value from an RxJS observable
get(count$); // 7
This only works in cases where it's able to give its current value synchronously either because
- it calls subscription callbacks synchronously, or
- it's a
BehaviorSubject
Otherwise it will throw an error
const click$ = fromEvent(document, "click");
get(click$); // THROWS AN ERROR -
// it doesn't make sense here as click$ is asynchronous
Semantic release
Release management is automated using semantic-release.