Home

Awesome

USM

Node CI npm

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/FrameworksNoneReduxMobXVuexAngular2+
Package Nameusmusm-reduxusm-mobxusm-vuexusm
Integrated Mutative🚫🚫
State TypeImmutableImmutableObservableObservableImmutable

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

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 or usm-vuex, you just use @computed, Since it is an observable model, its dependency collection is automatic.

When using usm or usm-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;
  }
}

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

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 in isEqual function for custom equal.

License

USM is MIT licensed.