A small, simple rx/observable based state-machine.
For applying to UI see the react bindings at @platform/state.react
Install
yarn add @platform/state
To work abstractly with a state library use the isolated type library:
yarn add @platform/state.types
Getting Started
Define your model
and mutation events
:
type IMyModel = {
count: number;
};
type MyEvent = IIncrementEvent | IDecrementEvent;
type IIncrementEvent = { type: 'TEST/increment'; payload: { by: number } };
type IDecrementEvent = { type: 'TEST/decrement'; payload: { by: number } };
type IStatustEvent = { type: 'TEST/status'; payload: { status: string } };
Create a new state-machine:
import { Store } from '@platform/state';
const initial: IMyModel = { count: 0, status: string };
const store = Store.create<IMyModel, MyEvent>({ initial });
Define a listener that mutates the state based on a specific event type (equivalent to a "reducer"):
store.on<ITestIncrementEvent>('TEST/increment').subscribe((e) => {
const count = e.state.count + e.payload.by;
const next = { ...e.state, count };
e.change(next); // UPDATE: New copy of state applied.
});
alternatively you can use "mutation like" syntax by passing a change function:
store
.on<ITestIncrementEvent>('TEST/increment')
.subscribe(e => {
e.change(draft => {
draft.count += e.payload.by; // UPDATE: New "structurally shared" immutable changes applied.
}));
});
This safely modifies an immutable clone of the state using "structural sharing" for efficiency (via immer).
Dispatch events to change state:
store.state; // => count === 0
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
store.state; // => count === 1
Listen for changes to the state and react accordingly, for instance updating UI that may be rendering the state.:
store.changed$.subscribe((e) => {
// ...
});
Add logic that reacts to events asynchronously and dispatches new update events (equivalent to an "epic"):
store
.on<IIncrementEvent>('TEST/increment')
.pipe(debounceTime(300))
.subscribe(async (e) => {
const status = await getNetworkStatus();
e.dispatch({ type: 'TEST/status', payload: { status } });
});