RxComp Store is a small Javascript module for RxComp, developed with Immer and RxJs as a simple alternative to Redux.
The store can however be used with any framework or VanillaJS.
lib & dependancy | size |
---|---|
rxcomp-store.min.js | |
rxcomp-store.min.js | |
rxcomp.min.js | |
rxcomp.min.js | |
rxjs.min.js | |
rxjs.min.js | |
immer.min.js | |
immer.min.js |
This library depend on RxComp Immer and RxJs
install via npm or include via script
npm install rxjs immer rxcomp rxcomp-store --save
For CDN, you can use unpkg
<script src="https://unpkg.com/@reactivex/rxjs@6.6.2/dist/global/rxjs.umd.min.js" crossorigin="anonymous" SameSite="none Secure"></script>
<script src="https://unpkg.com/immer@7.0.8/dist/immer.umd.production.min.js" crossorigin="anonymous" SameSite="none Secure"></script>
<script src="https://unpkg.com/rxcomp@1.0.0-beta.18/dist/umd/rxcomp.min.js" crossorigin="anonymous" SameSite="none Secure"></script>
<script src="https://unpkg.com/rxcomp-store@1.0.0-beta.18/dist/umd/rxcomp-store.min.js" crossorigin="anonymous" SameSite="none Secure"></script>
The global namespace for RxComp is rxcomp
import { CoreModule, Module } from 'rxcomp';
The global namespace for RxComp StoreModule is rxcomp.store
import { StoreModule } from 'rxcomp-store';
With the useStore
factory we create the immutable store with a default value.
The store will be consumed by a singleton service, and honoring the single-responsibility principle only a specific portion of the app data will be stored.
import { useStore } from 'rxcomp-store';
const { state$ } = useStore({ todolist: [] });
Subscribing to the state$
observable you always get the last immutable copy of the state
draft.
state$.subscribe(state => this.todolist = state.todolist);
The reducer
operator accept a reducer callback with the observable$
payload and a mutable draft of the state
as parameter.
When reducer returns, an immutable copy of the state will be pushed to the state$
observable through Immer.
const { reducer } = useStore({ todolist: [] });
observable$.pipe(
reducer((todolist, state) => state.todolist = todolist)
);
The retryState
operator will retry N times before throwing the error.
const { retryState } = useStore({ todolist: [] });
observable$.pipe(
retryState(3),
);
The catchState
operator is used to catch the error and store it in the immutable state.
const { catchState } = useStore({ todolist: [] });
observable$.pipe(
catchState(console.log),
);
You can then observe the state for errors.
state$.subscribe(state => this.error = state.error);
The cancel
method will interrupt all the currently running streams in the store.
This method will not abort the fetch requests.
const { cancel } = useStore({ todolist: [] });
// stop currently running streams
cancel();
The busy$
observable will store the busy flag in the immutable state and lock future calls until the observable completes.
const { busy$ } = useStore({ todolist: [] });
busy$().pipe(
switchMap(() => observable$),
);
You can then observe the busy state.
state$.subscribe(state => this.busy = state.busy);
While reloading the page, you may want to reload the previous state of the app.
First we have to initialize the store with a different StoreType
(the default is StoreType.Memory
) and give it a unique store name.
import { StoreType, useStore } from 'rxcomp-store';
const { cached$ } = useStore({ todolist: [] },
StoreType.Session, 'todolist'
);
With the cached$
observable we can retrieve the last saved state from sessionStorage
or localStorage
or cookie
.
cached$((state) => state.todolist)
- busy$ mark state as busy
- cached$ load data from cache
- reducer reduce the state to the new state
- retryState repeat the request N times then throw error.
- catchState catch the error and reduce the state to the errored state.
import { StoreType, useStore } from 'rxcomp-store';
const { busy$, cached$, reducer, retryState, catchState } = useStore(
{ todolist: [] },
StoreType.Session, 'todolist'
);
busy$().pipe(
switchMap(() =>
merge(cached$((state) => state.todolist), fromApi$).pipe(
reducer((todolist, state) => state.todolist = todolist),
retryState(),
catchState(console.log),
)
)
);
The select$
observable accept a reducer callback with an immutable copy of the state
as parameter and returns an immutable copy of a portion of the state
as observable.
const { select$ } = useStore({ todolist: [] });
const todolist$ = select$((state) => state.todolist);
The select
method works like the select$
observable but doesn't return an observable.
const { select } = useStore({ todolist: [] });
const todolist = select((state) => state.todolist);
The next
method accept a reducer callback with a mutable draft of the state
as parameter.
When reducer returns, an immutable copy of the state will be pushed to the state$
observable through Immer.
It works like the reduce
operator but doesn't return an observable.
const { next } = useStore({ todolist: [] });
next((state) => state.todolist = todolist))
The nextError
method will store the error
parameter in the immutable state.
It works like the catchState
operator but is intended to use in conjunction of classic catchError
operator.
const { nextError } = useStore({ todolist: [] });
catchError(error => nextError(error))
import { Browser, CoreModule, Module } from 'rxcomp';
import { StoreModule } from 'rxcomp-store';
import AppComponent from './app.component';
export default class AppModule extends Module {}
AppModule.meta = {
imports: [
CoreModule,
StoreModule
],
declarations: [],
bootstrap: AppComponent,
};
Browser.bootstrap(AppModule);
RxComp supports all browsers that are ES5-compliant (IE8 and below are not supported).
Pull requests are welcome and please submit bugs π
npm install
gulp
gulp build --target dist
Thank you for taking the time to provide feedback and review. This feedback is appreciated and very helpful π
If you find it helpful, feel free to contribute in keeping this library up to date via PayPal
- Luca Zampetti lzampetti@gmail.com
- Follow @actarian on Twitter
Changelog here.