Awesome
USM
USM is a universal state modular library, supports Redux(5.x), MobX(6.x), Vuex(4.x) and Angular(2.0+).
Motivation
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.
Support
Libraries/Frameworks | None | Redux | MobX | Vuex | Angular2+ |
---|---|---|---|---|---|
Status | ✅ | ✅ | ✅ | ✅ | ✅ |
Installation
To install usm
:
yarn add usm # npm install --save usm
And if you want to use Redux/MobX/Vuex, you just install usm-redux
/usm-mobx
/usm-vuex
.
Usage
-
Use
@state
to decorate a module state. -
Use
@action
to decorate a module method for state changes. -
Use
createStore
to create a store.
import { state, action, createStore } from 'usm';
// You can also use `usm-redux`, `usm-mobx`, or`usm-vuex`.
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 });
Examples
APIs
@state
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;
}
@action
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;
}
}
@computed/@computed()
It is used for computing derived data.
When use
usm-mobx
orusm-vuex
, you just use@computed
, Since it is an observable model, its dependency collection is automatic, And When useusm
orusm-redux
, you also use@computed
, Since it is an signal model, its dependency collection is also automatic.
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
usm
orusm-redux
, you can use@computed(depsCallback)
, The return value of thedepsCallback
is 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;
}
}
createStore()
Creates a usm
store that holds the complete shared state.
Arguments
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 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],
});
subscribe()
You can use subscribe()
to subscribe state changes in any class module.
For example,
class Counter {
constructor() {
subscribe(this, () => {
//
});
}
@state
count = { sum: 0 };
}
watch()
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;
}
watch
option supports passing inisEqual
function for custom equal.