Florian Spier
Posted on February 26, 2021
Hello from MiniRx Store
MiniRx Store is the new kid on the reactive state management block. MiniRx will help you to manage state at large scale (with Redux), but it also offers a simple form of state management: Feature Stores.
Let's get a quick overview:
Global
MiniRx Store is a global, application-wide solution to manage state in JavaScript and TypeScript applications.
Reactive
MiniRx is powered by RxJS and exposes state as RxJS Observables. The Observables emit when their selected state changes.
Scalable
MiniRx is a full-blown Redux Store: It includes actions, reducers, meta reducers, memoized selectors and redux dev tools support.
However, with MiniRx Feature Stores we can bypass Redux boilerplate: update state straight away with setState
.
MiniRx scales nicely with your state management requirements:
- Make hard things simple with the Redux API
- Keep simple things simple with the Feature Store API
Quick Links
- š¤ Learn more about MiniRx on the Docs site
- ā MiniRx on GitHub
- š See it in action on StackBlitz
The MiniRx DNA
NgRx
MiniRx is inspired by NgRx, which is a well known reactive Redux Store in the Angular world. MiniRx and NgRx look similar at first sight, but there are also important differences.
What do NgRx and MiniRx have in common?
- Their names sound similar: try to speak them out loudly ;)
- Powered by RxJS
- Both implement the Redux Pattern
- State and actions are exposed as RxJS Observable
What are the main differences?
- MiniRx has no Angular dependency and is framework agnostic
- MiniRx has Feature Stores to manage feature state without Redux boilerplate
- MiniRx is more lightweight and admittedly does not cover every crazy use case of state management
And it is these differences which explain the name "MiniRx".
Why MiniRx
NgRx and the Redux pattern are well-suited for managing state at a large scale. But almost every application contains also features which require only a simple form of state management. Then the Redux pattern with its actions and reducers quickly feels like overkill. It would be great to have a state management solution which looks and feels a lot like NgRx, but it has to support simple state management too: Scalable state management. It was time to create MiniRx Store:
Bypass Redux boilerplate with Feature Stores
MiniRx uses the Redux pattern to make state management explicit and predictable. The Redux pattern is very powerful, but it comes with some boilerplate code (mostly actions, reducers, dispatching actions). MiniRx allows us to bypass the Redux boilerplate for simple feature states: With Feature Stores we can manage feature state directly without actions and reducer (simply use setState
to update the feature state).
State Management which scales
For simple features we can use Feature Stores. And Feature Stores can be quite powerful actually: You can use memoized selectors (if you want to), you can create effects for API calls (if you want to). MiniRx scales nicely with your needs.
And you can always fall back to the Redux API in case that you have to manage huge and complex state.
Framework agnostic
NgRx is a great reactive Store, but it currently only works in Angular. There are also other frontend frameworks like Svelte which embrace reactivity. It would be cool to do NgRx-style state management in Svelte!
With MiniRx you can use whatever framework you want: you can build a framework-agnostic state management layer for Angular today and move it to Svelte (or any other frontend framework) tomorrow.
MiniRx Key Concepts
- The store is a single object which holds the global application state. It is the "single source of truth"
- State has a flat hierarchy and is divided into "feature states" (also called "slices" in Redux world)
- For each "feature state" we can decide to use the Redux API with actions and a reducer or the Feature Store API with
setState
- State is exposed as RxJS Observable (The Store exposes the global state, while a Feature Store exposes a specific feature state).
- State is read-only (immutable) and can only be changed by dispatching actions (Redux API) or by using
setState
(Feature Store API)
That was a long introduction! Let's dive into some code to see MiniRx in action...
Basic Tutorial
Store (Redux API)
MiniRx supports the classic Redux API with registering reducers and dispatching actions.
Observable state can be selected with memoized selectors.
import {
Action,
Store,
configureStore,
createFeatureSelector,
createSelector
} from 'mini-rx-store';
import { Observable } from 'rxjs';
// 1.) State interface
interface CounterState {
count: number;
}
// 2.) Initial state
const counterInitialState: CounterState = {
count: 1
};
// 3.) Reducer
function counterReducer(
state: CounterState = counterInitialState,
action: Action
): CounterState {
switch (action.type) {
case 'inc':
return {
...state,
count: state.count + 1
};
default:
return state;
}
}
// 4.) Get hold of the store instance and register root reducers
const store: Store = configureStore({
reducers: {
counter: counterReducer
}
});
// 5.) Create memoized selectors
const getCounterFeatureState = createFeatureSelector<CounterState>('counter');
const getCount = createSelector(
getCounterFeatureState,
state => state.count
);
// 6.) Select state as RxJS Observable
const count$: Observable<number> = store.select(getCount);
count$.subscribe(count => console.log('count:', count));
// 7.) Dispatch an action
store.dispatch({ type: 'inc' });
// OUTPUT: count: 1
// OUTPUT: count: 2
Feature Store API
Feature Stores allow us to manage feature state without actions and reducers.
The API of a Feature Store is optimised to select and update a feature state directly with a minimum of boilerplate.
import { FeatureStore } from 'mini-rx-store';
import { Observable } from 'rxjs';
// 1.) State interface
interface CounterState {
count: number;
}
// 2.) Initial state
const counterInitialState: CounterState = {
count: 11
};
export class CounterFeatureStore extends FeatureStore<CounterState> {
// Select state as RxJS Observable
count$: Observable<number> = this.select(state => state.count);
constructor() {
super('counterFs', counterInitialState);
}
// Update state with `setState`
inc() {
this.setState(state => ({ ...state, count: state.count + 1 }));
}
}
Use the "counterFs" feature store like this:
import { CounterFeatureStore } from "./counter-feature-store";
const counterFs = new CounterFeatureStore();
counterFs.count$.subscribe(count => console.log('count:', count));
counterFs.inc();
// OUTPUT: count: 11
// OUTPUT: count: 12
The state of a Feature Store becomes part of the global state
Every new Feature Store will show up in the global state with the corresponding feature key (e.g. "counterFs"):
store.select(state => state).subscribe(console.log);
//OUTPUT: {"counter":{"count":2},"counterFs":{"count":12}}
Notes
Demo
š See MiniRx Store in action on StackBlitz
The Demo uses both the Redux API and the Feature Stores:
- Todos: Feature Store
- Products and Cart: Redux
- User: Feature Store
More MiniRx Examples:
These popular Angular demo applications show the power of MiniRx:
- Angular Tetris with MiniRx
- Angular Jira Clone using MiniRx
- Coming soon: Angular Spotify using MiniRx
Documentation
Check out the docs for the full MiniRx API.
Show Your Support
If you like MiniRx: Give it a ā on GitHub
References
These projects, articles and courses helped and inspired me to create MiniRx:
- NgRx
- Akita
- Observable Store
- RxJS Observable Store
- Juliette Store
- Basic State Management with an Observable Service
- Redux From Scratch With Angular and RxJS
- How I wrote NgRx Store in 63 lines of code
- NGRX VS. NGXS VS. AKITA VS. RXJS: FIGHT!
- Pluralsight: Angular NgRx: Getting Started
- Pluralsight: RxJS in Angular: Reactive Development
- Pluralsight: RxJS: Getting Started
Posted on February 26, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.