USM is a universal state modular library, supports Redux(5.x), MobX(6.x), Vuex(4.x+), Pinia(3.x) and Angular(2.0+).
usm provides a generic state model that is class-first, which help us to be OOP at almost no cost and is compatible with the ecology of every state library.
When you don't want to learn the paradigm of any state library, usm can help you use any state library. When your project's business code is based on usm, the architecture will be more flexible.
| Libraries/Frameworks | None/Angular2+ | Redux | MobX | Vuex | Pinia |
|---|---|---|---|---|---|
| Package Name | usm | usm-redux | usm-mobx | usm-vuex | usm-pinia |
| Integrated Mutative | β | β | π« | π« | π« |
| State Type | Immutable | Immutable | Observable | Observable | Observable |
To install usm:
yarn add usm # npm install --save usmAnd if you want to use Redux/MobX/Vuex/Pinia, you just install usm-redux/usm-mobx/usm-vuex/usm-pinia.
-
Use
@stateto decorate a module state. -
Use
@actionto decorate a module method for state changes. -
Use
createStoreto create a store.
import { state, action, createStore } from 'usm';
// You can also use `usm-redux`, `usm-mobx`, `usm-vuex` or `usm-pinia`.
class Counter {
@state
count = { sum: 0 };
@action
increase() {
this.count.sum += 1;
}
}
const counter = new Counter();
const store = createStore({
modules: [counter],
});
counter.increase();
const newState = Object.values(store.getState())[0] as Counter;
expect(newState.count).toEqual({ sum: 1 });Define a shared state for a module, and you can use @state for decoration. When use usm-redux, the state is not allowed to be undefined.
For example,
class Counter {
@state
number = 0;
}All operations that change state must be in a method decorated by @action.
For example,
class Counter {
@state
number = 0;
@action
increase() {
this.number += 1;
}
}It is used for computing derived data.
When use
usm-mobx,usm-vuexorusm-pinia, you just use@computed, Since it is an observable model, its dependency collection is automatic.
When using
usmorusm-redux, you should also use@computed. Since it is a signal model, dependency collection is automatic. However, if you are using storage middleware (e.g., reactant-storage), you need to manually update the signal state to trigger reactivity.
For example,
class Counter {
@state
count = { sum: 0 };
@state
number = 0;
@action
increase() {
this.number += 1;
}
@computed
get sum() {
return this.count.sum + this.number;
}
}- If you want to manually control dependencies with
usmorusm-redux, you can use@computed(depsCallback), The return value of thedepsCallbackis an array of dependent value collections that tells the module that its getter will recompute when there is a change in any of the values in the value collections:
For example,
class Counter {
@state
count = { sum: 0 };
@state
number = 0;
@action
increase() {
this.number += 1;
}
@computed((that) => [that.count.sum, that.number])
get sum() {
return this.count.sum + this.number;
}
}Creates a usm store that holds the complete shared state.
options(object)modules(array): an array with all modules instances- [
strict] (boolean): enable strict mode
- [
preloadedState] (any): preloaded state - [
plugins/middleware] (any[]): vuex's plugins, pinia's plugins or redux's middleware
For example,
class Counter {
@state
number = 0;
@action
increase() {
this.number += 1;
}
}
const counter = new Counter();
const store = createStore({
modules: [counter],
});You can use subscribe() to subscribe state changes in any class module.
For example,
class Counter {
constructor() {
subscribe(this, () => {
//
});
}
@state
count = { sum: 0 };
}You can use watch() to observe a specific state changes in any class module.
For example,
class Counter {
constructor() {
watch(
this,
() => this.count.sum,
(newValue, oldValue) => {
//
}
);
}
@state
count = { sum: 0 };
}You can pass the option { multiple: true }, which will support watching multiple values.
For example,
class Counter {
constructor() {
watch(
this,
() => [this.count0, this.count1],
([newCount0, newCount1], [oldCount0, oldCount0]) => {
//
},
{
multiple: true,
}
);
}
@state
count0 = 0;
@state
count1 = 0;
}
watchoption supports passing inisEqualfunction for custom equal.
USM is MIT licensed.