8000 GitHub - hungry-egg/rx-state: Simple utilities for managing state with RxJS
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

hungry-egg/rx-state

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@hungry-egg/rx-state

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.

All functions are importable in the usual way:

import { atom } from "@hungry-egg/rx-state";

atom

This is very simply a container for any value that chang 8000 es. It's a thin wrapper around an RxJS BehaviorSubject.

Where you'd normally have a static value

const name = "Jerry";

you simply wrap with atom

const name$ = atom("Jerry");

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 = name$.subscribe((name) => console.log(`Name is now ${name}`));

so that when it's changed the callback is called

const name$.set("Tom"); // Logs "Name is now Tom"

To avoid memory leaks you should unsubscribe when finished

sub.unsubscribe();

The atom function returns a WritableAtom, which has the following methods:

const $name = atom("Jerry");

$name.get()                            // "Jerry"
$name.set("Tom")                       // sets value to "Tom" and notifies subscribers
$name.update(n => n + " Mander")       // set to "Jerry Mander" -
                                       //   use instead of set if you want to use the previous value

const uppercaseName$ = $name.pipe(     // pipe can be used as per usual in RxJS,
  map(n => n.toUpperCase()),           // and returns an RxJS observable
  ...
)
const lowercaseName$ = $name.map(n => n.toLowerCase()) // "map" is provided for convenience so you
                                                       //   don't need pipe, and returns a ReadonlyAtom

const $nameRO = $name.readonly()       // returns a read-only version of the atom (ReadonlyAtom)
$nameRO.get()    // "Jerry Mander"
$nameRO.set(...) // ERROR - METHOD DOESN'T EXIST!

$name.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.

If:

  • the atom is analogous to a Redux store (or part of),

then

  • combine is analogous to memoized selectors like Reselect.

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

Build

yarn build

Build on file change

yarn watch

Test

yarn test

About

Simple utilities for managing state with RxJS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published
0