MiniRx Store 6 has been released (2025-01-27)!
What's new?
- Connect external Observables with Feature Store or Component Store using the new
connect
method - Internal refactor: MiniRx Store uses @mini-rx/common which is also used by the MiniRx Signal Store
Read more in the CHANGELOG about the changes and the very few BREAKING CHANGES.
npm i mini-rx-store
MiniRx Store provides Reactive State Management, powered by RxJS. It is a highly flexible solution and scales with your state management needs:
- Manage global state at large scale with the Store (Redux) API
- Manage global state with a minimum of boilerplate using Feature Stores
- Manage local component state with Component Stores
MiniRx always tries to find the sweet spot between powerful, simple and lightweight.
🤓 Learn more about MiniRx on the docs site
🚀 See MiniRx in action:
- RxJS powered global state management
- State and actions are exposed as RxJS Observables
-
Store (Redux):
- Actions
- Reducers
- Meta Reducers
- Memoized Selectors
- Effects
-
mapResponse
operator: handle the side effect response in Effects - Support for ts-action: Create actions and reducers more efficiently
-
Feature Store: Manage feature state directly with a minimum of boilerplate:
-
setState()
update the feature state -
setInitialState()
initialize state lazily -
select()
select state from the feature state object as RxJS Observable -
effect()
run side effects like API calls and update feature state -
connect()
connect external Observables to your Feature Store -
undo()
easily undo setState actions (requires the UndoExtension) -
destroy()
remove the feature state from the global state object -
tapResponse
operator: handle the side effect response in Feature Storeeffect
-
-
Component Store: Manage state locally:
- Component Store is perfect for local component state
- Component Store has the same simple API as Feature Store (
setState
,select
, ...) - Component Store state is independent of the global state object
- Component Store is destroyable
-
Extensions:
- Redux DevTools Extension: Inspect global state with the Redux DevTools
- Immutable Extension: Enforce state immutability
- Undo Extension: Undo dispatched actions
- Logger Extension: console.log the current action and updated state
- Framework-agnostic: MiniRx works with any frontend project built with JavaScript or TypeScript (Angular, Svelte, React, Vue, or anything else)
- TypeScript support: The MiniRx API comes with TypeScript type definitions
-
Angular Integration: Use MiniRx Store the Angular way:
- Configure the Store with
StoreModule.forRoot()
- Add feature state with
StoreModule.forFeature()
- Inject
Store
andActions
- Configure the Store with
- State and actions are exposed as RxJS Observables
- Single source of truth: The Store holds a single object which represents the global application state
- The global 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
Store
(Redux) API with actions and reducers or the simplifiedFeatureStore
API -
Store
andFeatureStore
are different APIs for one and the same Redux Store - Use
ComponentStore
to manage state which is independent of the global state object - State is read-only (immutable) and can only be changed by dispatching actions (Redux API) or by using
setState
(Feature Store / Component Store)
Install from the NPM repository using npm:
npm install mini-rx-store
Let's dive into some code to see MiniRx in action. You can play with the tutorial code on StackBlitz.
MiniRx supports the classic Redux API with registering reducers and dispatching actions. Observable state can be selected with memoized selectors.
import {
Action,
Store,
configureStore,
createFeatureStateSelector,
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 = createFeatureStateSelector<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));
// OUTPUT: count: 1
// 7.) Dispatch an action
store.dispatch({ type: 'inc' });
// OUTPUT: count: 2
Read more in the MiniRx docs: Store (Redux)
With MiniRx Feature Stores we can manage feature state directly with a minimum of boilerplate.
import { FeatureStore } from 'mini-rx-store';
import { Observable } from 'rxjs';
// State interface
interface CounterState {
count: number;
}
// Initial state
const counterInitialState: CounterState = {
count: 11
};
// Extend FeatureStore and pass the State interface
export class CounterFeatureStore extends FeatureStore<CounterState> {
// Select state as RxJS Observable
count$: Observable<number> = this.select(state => state.count);
constructor() {
// Call super with the feature key and the initial state
super('counterFs', counterInitialState);
}
// Update state with `setState`
inc() {
this.setState(state => ({ count: state.count + 1 }));
}
}
Use the "CounterFeatureStore" like this:
import { CounterFeatureStore } from "./counter-feature-store";
const counterFs = new CounterFeatureStore();
counterFs.count$.subscribe(count => console.log('count:', count));
// OUTPUT: count: 11
counterFs.inc();
// 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}}
Read more in the MiniRx docs: Feature Store
Manage state locally and independently of the global state object. Component Store has the identical API as Feature Store.
import { ComponentStore } from 'mini-rx-store';
import { Observable } from 'rxjs';
// State interface
interface CounterState {
count: number;
}
// Initial state
const counterInitialState: CounterState = {
count: 111,
};
// Extend ComponentStore and pass the State interface
export class CounterComponentStore extends ComponentStore<CounterState> {
// Select state as RxJS Observable
count$: Observable<number> = this.select((state) => state.count);
constructor() {
// Call super with the initial state
super(counterInitialState);
}
// Update state with `setState`
inc() {
this.setState((state) => ({ count: state.count + 1 }));
}
}
Use the "CounterComponentStore" like this:
const counterCs = new CounterComponentStore();
counterCs.count$.subscribe(count => console.log('count:', count));
// OUTPUT: count: 111
counterCs.inc();
// OUTPUT: count: 112
Read more in the MiniRx docs: Component Store
See the basic tutorial on StackBlitz: MiniRx Store - Basic Tutorial
Demos:
-
Angular MiniRx Demo on GitHub
- See it live here
-
Svelte MiniRx Demo on GitHub
- See it live here
These popular Angular demo applications show the power of MiniRx:
- Angular Tetris with MiniRx on GitHub
- Angular Jira Clone using MiniRx on GitHub
- Angular Spotify using MiniRx on GitHub
More about MiniRx:
- Introducing MiniRx - Scalable reactive state management
- MiniRx Feature Store vs. NgRx Component Store vs. Akita
These projects, articles and courses helped and inspired us 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
MIT