UNPKG

3.13 kBMarkdownView Raw
1![Module](https://img.shields.io/badge/%40platform-state-%23EA4E7E.svg)
2[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
3[![NPM](https://img.shields.io/npm/v/@platform/state.svg?colorB=blue&style=flat)](https://www.npmjs.com/package/@platform/state)
4![banner](https://user-images.githubusercontent.com/185555/82968704-09aebc00-a022-11ea-9222-a334ef10b426.png)
5
6A small, simple [rx/observable](https://github.com/ReactiveX/rxjs) based state-machine.
7For applying to [UI](https://en.wikipedia.org/wiki/User_interface) see the [react](https://reactjs.org) bindings at [`@platform/state.react`](../state.react)
8
9<p>&nbsp;<p>
10
11## Install
12
13 yarn add @platform/state
14
15To work abstractly with a state library use the [isolated type library](../state.types):
16
17 yarn add @platform/state.types
18
19<p>&nbsp;<p>
20
21## Getting Started
22
23Define your `model` and mutation `events`:
24
25```typescript
26type IMyModel = {
27 count: number;
28};
29
30type MyEvent = IIncrementEvent | IDecrementEvent;
31type IIncrementEvent = { type: 'TEST/increment'; payload: { by: number } };
32type IDecrementEvent = { type: 'TEST/decrement'; payload: { by: number } };
33type IStatustEvent = { type: 'TEST/status'; payload: { status: string } };
34```
35
36<p>&nbsp;<p>
37
38Create a new state-machine:
39
40```typescript
41import { Store } from '@platform/state';
42
43const initial: IMyModel = { count: 0, status: string };
44const store = Store.create<IMyModel, MyEvent>({ initial });
45```
46
47<p>&nbsp;<p>
48
49Define a listener that mutates the state based on a specific event type (equivalent to a ["reducer"](https://redux.js.org/basics/reducers)):
50
51```typescript
52store.on<ITestIncrementEvent>('TEST/increment').subscribe((e) => {
53 const count = e.state.count + e.payload.by;
54 const next = { ...e.state, count };
55 e.change(next); // UPDATE: New copy of state applied.
56});
57```
58
59alternatively you can use "mutation like" syntax by passing a change function:
60
61```typescript
62store
63 .on<ITestIncrementEvent>('TEST/increment')
64 .subscribe(e => {
65 e.change(draft => {
66 draft.count += e.payload.by; // UPDATE: New "structurally shared" immutable changes applied.
67 }));
68 });
69```
70
71This safely modifies an immutable clone of the state using "structural sharing" for efficiency (via [immer](https://immerjs.github.io/immer)).
72
73<p>&nbsp;<p>
74
75Dispatch events to change state:
76
77```typescript
78store.state; // => count === 0
79store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
80store.state; // => count === 1
81```
82
83<p>&nbsp;<p>
84
85Listen for changes to the state and react accordingly, for instance updating UI that may be rendering the state.:
86
87```typescript
88store.changed$.subscribe((e) => {
89 // ...
90});
91```
92
93<p>&nbsp;<p>
94
95Add logic that reacts to events asynchronously and dispatches new update events (equivalent to an ["epic"](https://redux-observable.js.org)):
96
97```typescript
98store
99 .on<IIncrementEvent>('TEST/increment')
100 .pipe(debounceTime(300))
101 .subscribe(async (e) => {
102 const status = await getNetworkStatus();
103 e.dispatch({ type: 'TEST/status', payload: { status } });
104 });
105```
106
107<p>&nbsp;<p>
108<p>&nbsp;<p>