In the past year, more and more projects continue or start to use React and Redux development, which is a common front-end project solution in the front-end industry at present. However, as the development projects become more and more diversified, individuals have different feelings and ideas. Is it because there is already a common, familiar project technology stack that we have been using completely? Is there a more suitable solution? With the team recently working on a new project, the blogger began to wonder if it was possible to develop using alternative technologies. It can not only improve development efficiency, but also expand technical reserves and horizons. After investigation, Mobx was selected, and React+Mobx was finally used to build a new project. This paper summarizes and shares the complete process from technology selection to project realization, hoping to make progress together.

preface

When developing a Web application using React, you can use this.setState() and this.state to handle and access state within the React component. However, as the project grows larger and the state becomes more complex, communication between components needs to be considered.

  1. A state needs to be shared across multiple components (access, update);
  2. Interactions within one component need to trigger status updates for other components;

To address these issues, the React component development practice recommends promoting the public component state:

Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor
Often multiple components need to handle the same state, and we recommend promoting the shared state to their common nearest ancestor component.For more details

When increasingly complex project, we found that merely improve state already can’t adapt to such a complicated state management, program state becomes difficult to synchronize, operation, there is callback, publish, subscribe, this means that we need a better way of state management, and then introduced the state management library, such as story, Mobx, Jumpsuit, Alt, js, etc.

State management

State management libraries, whether Redux or Mobx, are essentially designed to solve the problem of messy state management and inefficient synchronization. They all support:

  1. Unified maintenance and management of application status;
  2. A state has only one trusted data source (usually named store for state container);
  3. The mode of operation update status is unified and controllable (usually action mode provides the way of update status);
  4. Connect the Store and React components, for examplereact-redux.mobx-react; Typically, using the state management library, we divide the React component into two business categories:
    1. Container Components: Responsible for processing specific business and state data, passing business or state handling functions into the presentation Components;
    2. Presentation Components: Responsible for presenting the view and calling the handler passed in within the view interaction callback;

Mobx VS Redux

Redux dominates the React app state management library, while Mobx dominates the React app state management library. Why would we choose Mobx over Redux?

Mobx and Redux are JavaScript application state management libraries that work with frameworks or libraries like React, Angular, VueJs, etc., rather than being limited to a specific UI library.

Redux

To introduce Redux, we have to talk about Flux:

Flux is the application architecture that Facebook uses for building client-side Web applications.It’s more of a pattern rather than a formal framework
Flux is an application architecture that Facebook uses to develop client-server Web applications. It is more of an architectural pattern than a specific framework.Break down Flux.

Redux is more of an implementation that follows the Flux pattern. It is a JavaScript library that focuses on the following aspects:

  1. Action: a JavaScript object that describes information about an Action, including the type attribute and the payload attribute:
    1. Type: Action type.
    2. Payload: payload data.
  1. Reducer: Define how the application state responds to different actions and how to update the state.
  2. Store: Manages actions and reducer and their related objects, providing the following functions:
    1. Maintains application state and supports access state (getState());
    2. Support to monitor action distribution, update status (dispatch(action));
    3. Support store changes (subscribe(listener));
  1. Asynchronous flow: All Redux changes to store state should be triggered by action, and asynchronous tasks (usually business or data acquisition tasks) are no exception. In order to avoid mixing business or data related tasks into React components, other frameworks need to be used to manage asynchronous task flows, such asredux-thunk.redux-sagaAnd so on;

Mobx

Mobx is a Transparently Functional Reactive Programming (TFRP) state management library that makes state management simple and scalable:

Anything that can be derived from the application state, should be derived. Automatically.
Any data originating from application state should be retrieved automatically.

Its principle is shown as follows:


  1. Action: Defines the Action function that changes the state, including how to change the state;
  2. Store: Centrally manages module states and actions.
  3. Derivation: Data derived from the application state without any other impact is called Derivation, and Derivation exists in the following situations:
    1. User interface;
    2. Derived data;

There are two main types of derivation:

      1. Computed Values: Computed Values can always be obtained from the current observable state using pure functions;
      2. Reactions: Reactions are the side effects that need to happen automatically when the state changes — in this case, make it read or write;
import {observable, autorun} from 'mobx';

var todoStore = observable({
    /* some observable state */
    todos: [],

    /* a derived value */
    get completedCount() {
        return this.todos.filter(todo => todo.completed).length;
    }
});

/* a function that observes the state */
autorun(function() {
    console.log("Completed %d of %d items",
        todoStore.completedCount,
        todoStore.todos.length
    );
});

/* ..and some actions that modify the state */
todoStore.todos[0] = {
    title: "Take a walk",
    completed: false
};
// -> synchronously prints: 'Completed 0 of 1 items'

todoStore.todos[0].completed = true;
// -> synchronously prints: 'Completed 1 of 1 items'
Copy the code

Functional and object oriented

Redux follows more of the philosophy of Functional Programming (FP), while Mobx thinks more in terms of objects.

Redux advocates the compilation of functional code, such as reducer is a pure function, as follows:

(state, action) => { return Object.assign({}, state, { ... })}Copy the code

A pure function that takes an input and then outputs the result without any other effects, including the parameters received; Always output the same result for the same input.

Mobx’s design is more oriented towards OOP and Reactive Programming, usually wrapping state as an observable so that we can use all the capabilities of the observable and get updates automatically when the state object changes.

Single store and multiple store

Store is where apps manage their data. In Redux apps, we always put all shared app data in one big store, whereas Mobx usually divides app state by module and manages it in multiple independent stores.

JavaScript objects and observables

Redux stores data as JavaScript native objects by default, while Mobx uses observables:

  1. Redux needs to manually track all state object changes;
  2. In Mobx, observables can be listened on and automatically triggered when they change;

Immutable and Mutable

Redux state objects are usually Immutable:

switch (action.type) {
  case REQUEST_POST:
    return Object.assign({}, state, {
      post: action.payload.post
    });
  default:
    return state;
}
Copy the code

We can not operate the state object directly, but always return a new state object on the basis of the original state object, so that it is very convenient to return the application of a state; In Mobx, you can update the status object directly with the new value.

Mobx – react and react – story

To connect the React and Redux applications, use the Provider and connect provided by Redux:

  1. ProviderInject Store into React;
  2. connectIs responsible for injecting store state into the container component and passing the selected state as the container component props;

For Mobx, there are also two steps:

  1. ProviderUse:mobx-reactTo provide theProviderInject all stores into the application;
  2. useinjectInject a particular store into a component, which can pass state or action; Then use theobserverMake sure the component responds to Observable changes in the store, i.e., store updates, and component views responsive updates.

Why Mobx was chosen

  1. Low learning cost: Basic Mobx knowledge is very simple, learning official documents and sample code for half an hour to build a new project instance; However, Redux is more complicated and has many processes. It needs to be configured, create store, compile Reducer and action. If asynchronous tasks are involved, it also needs to be introducedredux-thunkorredux-sagaThe Mobx process is much simpler than writing extra code and does not require additional asynchronous processing libraries;
  2. Object-oriented programming: Mobx supports object-oriented programming, which we can use@observable and @observerTo make JavaScript objects responsive by object-oriented programming; Redux’s best recommendation is to follow functional programming, which Mobx also supports;
  3. Less template code: Compared with various Redux template code, such as actionCreater, Reducer, Saga/Thunk, Mobx does not need to write such template code;

Possible reasons for not choosing Mobx

  1. Too much freedom: Mobx provides very few conventions and template code, which leads to very free development code writing. If you do not make some conventions, it is easy to cause the team code style to be inconsistent. Therefore, when there are many team members, you do need to add some conventions.
  2. Scalable, maintainable: You might wonder if Mobx will be able to adapt to late-stage projects. Indeed Mobx is more suitable for use in small to medium sized projects, but that doesn’t mean it can’t support a large project, the key lies in a large project usually need to pay special attention to expanding, maintainability, by contrast, standard Redux have more advantages, and Mobx more freedom, need we make some rules to ensure that the project late to expand, maintain ease;

Code comparison

Next, let’s simply implement the same application using Redux and Mobx and compare their code to see how each performs.

architecture

In a Redux application, we first need to configure, create a store, use the Redux-Thunk or Redux-Saga middleware to support asynchronous action, and then use the Provider to inject the store into the application:

// src/store.js import { applyMiddleware, createStore } from "redux"; import createSagaMiddleware from 'redux-saga' import React from 'react'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { composeWithDevTools } from 'redux-devtools-extension'; import rootReducer from "./reducers"; import App from './containers/App/'; const sagaMiddleware = createSagaMiddleware() const middleware = composeWithDevTools(applyMiddleware(sagaMiddleware)); export default createStore(rootReducer, middleware); / / SRC/index. Js... ReactDOM.render( <BrowserRouter> <Provider store={store}> <App /> </Provider> </BrowserRouter>, document.getElementById('app') );Copy the code

Mobx apps can inject all stores directly into the app:

import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'mobx-react'; import { BrowserRouter } from 'react-router-dom'; import { useStrict } from 'mobx'; import App from './containers/App/'; import * as stores from './flux/index'; // set strict mode for mobx // must change store through action useStrict(true); render( <Provider {... stores}> <BrowserRouter> <App /> </BrowserRouter> </Provider>, document.getElementById('app') );Copy the code

Injection of Props

Story:

/ / SRC/containers/Company. Js... class CompanyContainer extends Component { componentDidMount () { this.props.loadData({}); Render () {return <Company infos={this.props. Infos} loading={this.props. Loading} />}}... // function for injecting state into props const mapStateToProps = (state) => { return { infos: state.companyStore.infos, loading: state.companyStore.loading } } const mapDispatchToProps = dispatch => { return bindActionCreators({ loadData: loadData }, dispatch); } // injecting both state and actions into props export default connect(mapStateToProps, { loadData })(CompanyContainer);Copy the code

Mobx:

@inject('companyStore')
@observer
class CompanyContainer extends Component {
  componentDidMount () {
    this.props.companyStore.loadData({});
  }
  render () {
    const { infos, loading } = this.props.companyStore;
    return <Company
      infos={infos}
      loading={loading}
    />
  }
}
Copy the code

Define actions/Reducer, etc

Story:

/ / SRC/flux/Company/action. Js... Export function fetchContacts(){return dispatch => {dispatch({type: 'FREQUEST_COMPANY_INFO', payload: {}})}}... // src/flux/Company/reducer.js const initialState = {}; function reducer (state = initialState, action) { switch (action.type) { case 'FREQUEST_COMPANY_INFO': { return { ... state, contacts: action.payload.data.data || action.payload.data, loading: false } } default: return state; }}Copy the code

Mobx:

// src/flux/Company/store.js import { observable, action } from 'mobx'; class CompanyStore { constructor () { @observable infos = observable.map(infosModel); } @action loadData = async(params) => { this.loading = true; this.errors = {}; return this.$fetchBasicInfo(params).then(action(({ data }) => { let basicInfo = data.data; const preCompanyInfo = this.infos.get('companyInfo'); this.infos.set('companyInfo', Object.assign(preCompanyInfo, basicInfo)); return basicInfo; })); } $fetchBasicInfo (params) { return fetch({ ... API.getBasicInfo, data: params }); } } export default new CompanyStore();Copy the code

Asynchronous Action

With Redux, we need to add redux-Thunk or Redux-saga to support asynchronous action, which requires additional configuration and writing template code, whereas Mobx does not require additional configuration.

Redux-saga main code is:

// src/flux/Company/saga.js // Sagas // ------------------------------------ const $fetchBasicInfo = (params) => { return fetch({ ... API.getBasicInfo, data: params }); } export function *fetchCompanyInfoSaga (type, body) { while (true) { const { payload } = yield take(REQUEST_COMPANY_INFO) console.log('payload:', payload) const data = yield call($fetchBasicInfo, payload) yield put(receiveCompanyInfo(data)) } } export const sagas = [ fetchCompanyInfoSaga ];Copy the code

Some ideas

Whether front-end or back-end, encountered problems, most of the time maybe we are always used to recommend has been widely promoted use, habit, familiar with it is easy to become a natural, we should go further to think, the appropriate is better.

Of course, we should avoid the inference that “Redux is more formal, more reliable, Redux should be used” or “There are too many Redux templates, too complex, Mobx should be used”. These are all relative. Each framework has its own implementation, features, and application scenarios. However, if you are familiar with the process, you will be more able to grasp some of its basic/core concepts and have more experience and comprehension when using it. Mobx’s simplicity, hiding most of its stuff, and not being able to get to its core/basic ideas without special research, may keep developers stuck at the adoption level.

So whether it’s a technology stack or a framework. There is no absolute comparison of libraries as to what we should choose, what we should throw away, what problems they solve, what concerns they address, or how they are implemented, what their strengths and weaknesses are, and which one is more appropriate for the current project and the future of the project.

reference

  1. An in-depth explanation of Mobx
  2. Redux & Mobx
  3. Mobx
  4. Redux vs Mobx