If you are interested in how Redux/Mobx/Vuex state libraries can make OOP design better, this article provides a complete generalization of OOP for front-end state libraries.

motivation

Due to the increasing complexity of front-end single-page application development, when we use React/Vue, we have to use some state management or state containers (collectively referred to as state libraries below) to develop complex apps. Meanwhile, we also need a more modular model.

Whether it is Redux/MobX/Vuex or Angular’s own state management, the modularity of state libraries has been a new requirement in the field of front-end development for complex systems in recent years. Redux is a container of predictable state with immutable data structures. MobX is an observable state management library. Vuex is a centralized state management library with visibility in Vue. Angular already has its own implementation for modularity, but other state management libraries increasingly need to handle this new requirement in complex front-end projects.

In this article, let’s explore a new OOP modular design that has universal support for mainstream state management libraries.

Generalize the status module

Object-oriented programming (OOP) is often used in the architectural design of large and medium front-end projects, and the following questions are often asked when deciding on a state management library:

  • Is Redux or MobX better for React?
  • Is Redux suitable for OOP?
  • How to weigh the advantages and disadvantages of MobX Observable in React?
  • How OOP is Vuex in Vue?

In addition, in most cases, the front-end architecture is tightly coupled to state management. Once you choose a state management library, it is difficult to switch to another library without major refactoring. Therefore, any system that uses this architecture must also use the same state library. But a better front-end architecture design is flexible and extensible. Especially for designs designed to achieve integration purposes, it is important to adapt to the target environment and SDK architecture. To create modules that work with the main Z frameworks (React+Redux/React+MobX/Vue+Vuex/Angular), we need to generalize the state module design.

Design goals

  • OOP design based on Redux/MobX/Vuex state libraries is also the most important, especially for Vue and React.
  • Are the encapsulated OOP designs simple enough to use, and are they flexible enough?
  • From a DDD perspective, the dependencies between complex domain modules require IoC, and the startup logic between them has dependencies, so there must be some kind of event mechanism or module lifecycle introduced.

To solve these problems, generic OOP encapsulation and modular standardization of life cycles or event mechanisms become indispensable.

Propose solutions

Based on this universal concept, we propose a new universal state module library — USM.

First, it should be able to solve OOP designs based on state libraries such as Redux/MobX/Vuex.

Let’s start with a typical Redux counter example:

import { createStore } from 'redux';

function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      returnstate; }}const store = createStore(counter)

store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'DECREMENT' })
Copy the code

USM supports Redux, MobX, Vuex, and Angular. It provides four subpackages usm, USm-redux, USm-MOBx, and USm-vuex. Here is an example counter using usM-redux:

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

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();
Copy the code

The implementation of the same counter above is based on an object-oriented paradigm. ES6 class syntax is straightforward and concise to use. If this design can be applied to any state management library in use, it will undoubtedly lead to a more flexible and friendlier development experience for developers, as well as better readability and maintainability.

Usm-redux is used in this example, which implements IMMUTABLE data from the mutable operation based on Immer.

I have to admit that Redux is definitely one of the best immutable state libraries out there, and I’m not going to discuss some of Redux’s weaknesses here, but rather how Redux can be used for better OOP design. We want redux-based models to be more intuitive and concise, like the OO example of ES6+ class Counter mentioned above. If this OO paradigm is also a generalized state model, a better unified state library encapsulation, This certainly leads to a more flexible and friendlier programming experience for developers (as well as easier to read/maintain, etc.). Usm solves these problems.

React: usm-redux: usm-redux: usm-redux

// index.js
export const counter = Counter.create();

ReactDOM.render(
  <Provider store={counter.store}>
    <App />
  </Provider>.document.getElementById('root'));Copy the code
// app.js import { connect } from 'react-redux'; import { counter } from './'; export default connect( state => ({ count: state.count }) )( props => <div> <button onClick={() => counter.increase()}>+</button> {props.count} <button onClick={()  => counter.decrease()}>-</button> </div> );Copy the code

Here is an example of a connection using mobx-react and usm-mobx:

// index.js

export const counter = Counter.create();

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
Copy the code
// app.js
import { observer } from 'mobx-react';
import { counter } from './';

export default observer(() =>
  <div>
    <button onClick={() => counter.increase()}>+</button>
    {counter.count}
    <button onClick={() => counter.decrease()}>-</button>
  </div>
);
Copy the code

The combination of usM-redux +react-redux and USm-mobx + React-redux and React is enough to prove that the core business logic of the state module is the same, even if the connectors used are different. This is the core principle behind our proposed generalization of state modules.

USM currently supports Redux, MobX, Vuex and Angular.

features

  • Generalize the status module
  • Standardize the module life cycle
  • Optional event system
  • Support stateless minimization model
  • Support Redux/MobX/Vuex/presents

A decorator

Usm provides @state for wrapping a variable with state and @Action for wrapping a function that changes state (the last parameter passed to the function is the current state object), except that it is no different from a normal class-wrapped OO module, and it also provides a generic @computed.

class Shop extends Module {
  @state goods = [];
  @state status = 'close';

  @action
  operate(item, status, state) {
    state.goods.push(item);
    state.status = status;
  }
  //this.operate({ name: 'fruits', amount: 10 }, 'open');
}
Copy the code

Module life cycle

Usm provides five lifecycle functions that support asynchrony:

  • moduleWillInitialize
  • moduleWillInitializeSuccess
  • moduleDidInitialize
  • moduleWillReset
  • moduleDidReset

Their running order is shown below:

In particular, USM provides life cycles because in most complex domain-module scenarios, these module life cycles can be used to coordinate dependencies during module initialization. Of course, they can be set up to save when you don’t have to use them.

The ideal architectural design

In a complex front-end module system, this may be a typical modular architecture design, which consists of the following parts:

  • The life cycle
  • Store subscriber
  • The event system
  • State
  • Rely on the module
  • The domain model

This is just a suggestion here, and perhaps some architectural application scenarios may be such that the design model can be extended or deleted.

conclusion

USM is a modular design that wants to tie together the differences in using Redux, MobX, and Vuex in combinations of different view layers such as React, Vue, and Angular. It is designed to help you build libraries that can be used with any front-end architecture.

When you’re developing with React+Redux/React+MobX/Vue+Vuex or a combination of frameworks, hopefully USM is a good choice for modularity in your application, especially since it might be an important piece of the modularity puzzle that you’re missing when you’re building UI libraries like React/Vue.

In other words, if you use USM for OOP architecture, not only can your system reduce the number of Boilerplates in different state libraries, but libraries like Redux that have a lot of Boilerplates should help a lot. Most importantly, USM makes modularization of OOP architectures simple and intuitive. Usm can even make your business code compatible with various state libraries, whether Redux/MobX/Vuex or Angular, and if your UI component library happens to be React/Vue/Angular compatible, Your app will use React/Vue/Angular quickly and seamlessly.

USM allows you to share business logic libraries across frameworks, regardless of the framework they use.

Finally, we might ask a question worth pondering:

From an OOP perspective, does the choice of a front-end state library really matter that much?