jo0ger / usm

🏖Concise & flexible state model for Redux/MobX/Vuex, etc.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

USM

Travis npm

USM is a universal state modular library. It can help you make more concise OOP when using some state library, and it currently supports Redux, MobX, Vuex and Angular.

You don't have to learn so many state libraries, one is enough.

Table of Contents

Support

Libraries/Frameworks None Redux MobX Vuex Angular2+
Status

Features

  • Universal State Management
  • Standardized Module Lifecycle
  • Optional Event System
  • Support Stateless Model
  • Support React/Vue/Angular

Usage

To install usm:

yarn add usm # npm install --save usm

And if you want to use Redux/MobX/Vuex, just install usm-redux/usm-mobx/usm-vuex correspondingly.

Pros

Here is the Redux todo example:
import { createStore, combineReducers } from 'redux'

// action
let nextTodoId = 0
const addTodo = text => {
  return {
    type: 'ADD_TODO',
    id: nextTodoId++,
    text
  }
}

const setVisibilityFilter = filter => {
  return {
    type: 'SET_VISIBILITY_FILTER',
    filter
  }
}

const toggleTodo = id => {
  return {
    type: 'TOGGLE_TODO',
    id
  }
}

// reducers
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    case 'TOGGLE_TODO':
      return state.map(todo =>
        (todo.id === action.id) 
          ? {...todo, completed: !todo.completed}
          : todo
      )
    default:
      return state
  }
}

const visibilityFilter = (state = 'SHOW_ALL', action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

const todoApp = combineReducers({
  todos,
  visibilityFilter
})

With usm-redux it can be more concise:

import Module, { state, action } from 'usm-redux';

class TodoList extends Module {
  @state todos = [];
  @state visibilityFilter = 'SHOW_ALL';
  nextTodoId = 0;

  @action
  add(text, state) {
    this.nextTodoId++;
    state.todos.push({
      text,
      id: this.nextTodoId,
      completed: false,
    });
  }

  @action
  toggle(id, state) {
    const todo = state.todos.find(todo => todo.id === id);
    todo.completed = !todo.completed;
  }

  @action
  setVisibility(filter, state) {
    state.visibilityFilter = filter;
  }
}

USM will help you a lot on object-oriented programming using Redux, Vuex, MobX, etc.

Articles

Examples

class Counter extends Module {
  @state count = 0;

  @action
  increase(state) {
    state.count += 1;
  }

  @action
  decrease(state) {
    state.count -= 1;
  }
}

const counter = Counter.create();

counter.increase();
counter.decrease();

Using different wrapper libraries:

import Module, { action, state } from 'usm'; // using Native Module/Angular
import Module, { action, state } from 'usm-redux'; // using Redux
import Module, { action, state } from 'usm-mobx'; // using MobX
import Module, { action, state } from 'usm-vuex'; // using Vuex

More examples:

APIs

Decorators

usm provides different kinds of decorators:

  • @state to wrap a variable with a state.
  • @action is used to wrap a function that changes state (the last parameter passed in by the function is always the current state object).
  • @computed is used in state computed, and it must be an computed function array(You can also use it for a getter with usm-vuex or usm-mobx).
class Shop extends Module {
  @state goods = [];
  @state status = 'close';

  @action
  operate(item, status, state) {
    state.goods.push(item);
    state.status = status;
  }

  @action
  setList(list, state) {
    state.goods = list;
  }

  async getRemoteData(url) {
    const response = await fetch(url);
    const data = await response.json();
    this.setList(data);
  }

  @computed
  shortages = [
    () => this.goods,
    (goods) => goods.filter(item => item.amount < 5)
  ];
}

Module lifecycle

usm provides these lifecycle hooks:

  • moduleWillInitialize
  • moduleWillInitializeSuccess
  • moduleDidInitialize
  • moduleWillReset
  • moduleDidReset

Note:

If you need to run the USM-based module directly, you must use the module's create method, just like this:

class Something extends Module {}
const thing = Something.create();

And if the module is just initialized, the internal lifecycle hooks will not be called. If a module depends on more than one modules, these modules have to be initialized before the curent module.

class Foo extends Module {}
class Bar extends Module {}
class FoobarFactory extends Module {}
const foo = new Foo();
const bar = new Bar({
  modules: {
    foo,
  },
});
const foobarFactory = FoobarFactory.create({
  modules: {
    foo,
    bar
  },
});

USM does not provide module dependency management, you have to manage dependencies youself like the example above. And you are stronglly recommended to introduct external dependency injection libs to manage dependencies automatically, it will make you life easir. For example, using InversifyJS. If you use Angular, you will be able to use Angualr's dependency injection directly.

FAQ

1. Can I continue to use the Redux or Vuex plug-in if I use usm?

Of course, you can, but usm will soon define its own plug-in APIs to ensure that there is a universal plug-in that can be used.

usm-redux using Redux's middleware example:

import { applyMiddleware, createStore } from 'redux';

class ModuleWithMiddleware extends Module {
  static _generateStore(_, { reducers }) {
    return createStore(reducers, applyMiddleware(...reduxMiddlewares));
  }
}

usm-vuex using Vuex's plugin example:

class ModuleWithPlugin extends Module {
  plugins = [...vuexPlugins];
}

2. Does it look like usm-redux is a state library of mutable type?

Yes, because Redux immutable operation is not convenient enough, so usm introduced immer. Generally, single-action items are less than 50K, then it comes with a tiny loss of performance which can be ignored most of the time. For more on performance issues that immer brings, please check it out here.

3. How do you ensure that you use usm to switch between different states libraries(usm-redux/usm-vuex/usm-mobx) and that they are running consistently?

usm is not a state library, we are trying to turn it into a standardized state library runner, usm defines only generic modules. Based on such a module, any state library based on the usm encapsulation can run well.

4. Can usm-redux support redux-devtools or usm-vuex support vue-devtools?

Of course, They fully support these devtools.

About

🏖Concise & flexible state model for Redux/MobX/Vuex, etc.

License:MIT License


Languages

Language:TypeScript 85.7%Language:JavaScript 14.3%