RX State
Background
After working with Angular for a few years now, I have come to appreciate how people are able to build Angular specific libraries that accomplish some major feats that make my job easier. I have come to a place where Angular no longer is a part of my best interests and I am trying to find ways to spread some concepts to a framework-agnostic ecosystem that way I don't have to rewrite a majority of application logic just to transfer to a new framework.
As a result of my research and experimentation, I have come to appreciate RXJS in a new way. Due to this new-found appreciation I have come up with a simple state management platform based off of functional programming (slightly) and Redux to compliment those who wish to have a basic-yet-robust state management system without locking into any one framework.
I hope that through my experimentation in this realm that others can benefit from my findings.
That being said, let's get into the meat of the library:
Getting Started
This library is heavily influenced by NGXS and Redux to bring those patterns into a framework-agnostic environment. You should be able to easily throw some code into any framework you want and bind your state and events in any way you please.
To get started: npm i @jwhenry/rx-state --save
Dependencies: rxjs
, yes that is all.
Event System
The event system is influenced by pub-sub models, Redux, and NGXS.
You are provided a global events
constant that you can subscribe to in order to observe actions that take place.
You may also set EventOptions.log
to true if you wish to watch these actions take place.
The event stream is the source of all state actions or any arbitrary behavior you want to trigger in your application.
An eventsOf
function will allow you to provide a list of names or objects with an event
property to listen specifically for.
When reading about how to create states, you will see how this is used to trigger state mutations within the ecosystem.
Creating States
States act as reducers from Redux or State in NGXS. Their purpose is to handle the state mutation or other dispatching based on the new state values
Example Actions:
export const SomeAction = 'some.action';
export declare type SomePayload = {value:string};
export interface ISomePayload {
value:string
}
export class StoreSession {
static readonly event = 'session.store';
constructor(public data: SessionModel) {
}
}
export class ClearSession {
static readonly event = 'session.clear';
}
Example State:
States can be constructed with as little boilerplate as necessary.
- By using the
stateof
function, you can build NGXS-like stores with a context to wrap even more boilerplate nicely. - You can still access other states by using the
selectSnapshot
anddispatch
functions. - The purpose of
stateOf
is to make your state fully self-aware and self-contained.
import {dispatch, start, stateOf, removeAllStates} from "@jwhenry/rx-state";
import {StoreSession, SessionModel, ClearSession} from "./session-data";
export const SessionState = stateOf<SessionModel>('session', (onEvent, context) => {
if (!context.getState()) {
context.setState(new SessionModel()); // Set a default value
}
onEvent(StoreSession, data => {
// The data will be SessionModel according to the above examples
context.setState(data);
});
onEvent(ClearSession, () => {
// Clear all states because we don't want to retain anything for users who are not logged in
removeAllStates();
// OR remove a specific state
context.setState(new SessionModel());
})
}, true); // last argument is whether we persist the state
Observing State
You can observe state in multiple ways:
- Watch the whole state
-
import {state} from "@jwhenry/rx-state"; state.subscribe(state => console.log(state));
-
- Watch a specific state
-
import {select} from "@jwhenry/rx-state"; select('session').subscribe(session => console.log(session));
-
- Grab a snapshot of a specific state
-
import {selectSnapshot} from "@jwhenry/rx-state"; console.log(selectSnapshot('session'));
-
Starting and Stopping States
In order to start your states, you must register them and when ready, start them:
import {register, startStates, stopStates} from "@jwhenry/rx-state";
register(SomeState, SomeOtherState);
startStates();
// Start a state late
start(SomeLazyState);
// After some time or when you want to kill state listeners
stopStates();
Feedback and Future
If there are desired features or feedback that may spark a larger effort on this project, this may become something that developers can use to replace some framework-specific state management tools.