Do you need Mobx or Redux?

foreword

When we use React to develop web applications, within the React component, we can use this.setState()and this.stateprocess or access the state within the component, but as the project becomes larger and the state becomes more complex, we usually need to consider the communication between components, mainly including the following two points:

  1. A certain state needs to be shared (accessed, updated) among multiple components;
  2. Interactions within a component need to trigger state updates of other components;

Regarding these issues, the React component development practice recommends that the public component state be promoted:

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 shared state to their common nearest ancestor component. View more details

When the project becomes more complex, we find that just improving the state can no longer adapt to such complex state management, and the program state becomes more difficult to synchronize, operate, and call back, publish, and subscribe everywhere, which means that we need better state management. In this way, state management libraries are introduced, such as Redux , Mobx , Jumpsuit , Alt.js , etc.

state management

The essence of state management libraries, whether it is Redux or Mobx, is to solve the problem of state management confusion and inability to synchronize effectively. They all support:

  1. Unified maintenance and management of application status;
  2. There is only one source of trusted data for a state (usually named store, referring to the state container);
  3. The way of operating the update state is unified and controllable (usually, the way to update the state is provided in the form of action);
  4. Supports connecting the store with React components, such as react-redux; mobx-reactusually after using the state management library, we divide React components into two categories from business:
    1. Container Components: Responsible for processing specific business and status data, and passing business or status processing functions into presentational components;
    2. Presentation Components: responsible for displaying the view, and calling the incoming processing function in the view interaction callback;

Mobx VS Redux

At present, Redux is the overlord in the React application state management library, and Mobx is a prince. Why should we choose Mobx instead of continuing to use Redux, then we need to compare their similarities and differences.

Mobx and Redux are both JavaScript application state management libraries, and both are suitable for frameworks or libraries such as 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 Facebook's application architecture for developing client-server web applications, and it's more of an architectural pattern than a specific framework. Explain Flux in detail .

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 action-related information, mainly including type attribute and payload attribute:
    1. type: action type;
    2. payload: payload data;
  2. Reducer: Define how the application state responds to different actions and how to update the state;
  3. Store: An object that manages actions and reducers and their relationships, mainly providing the following functions:
    1. Maintain application state and support access to state (getState());
    2. Supports monitoring the distribution of actions and updating the status (dispatch(action));
    3. Support subscription store changes (subscribe(listener));
  4. Asynchronous flow: Since all changes to the store state in Redux should be triggered by actions, asynchronous tasks (usually business or data acquisition tasks) are no exception, and in order not to mix business or data-related tasks into React components, just Need to use other frameworks to manage asynchronous task processes, such as redux-thunk, redux-sagaetc.;

Mobx

Mobx is a state management library for Transparently Functional Reactive Programming (TFRP) 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 fetched automatically.

Its principle is shown in the figure:

Mobx Philosophy

  1. Action: Define the action function that changes the state, including how to change the state;

  2. Store: centralized management of module state (State) and action (action);

  3. Derivation: Data derived from the application state without any other influence, we call it derivation, and derivation exists in the following cases:

    1. User Interface;

    2. derived data;

      There are two main types of derivatives:

      1. Computed Values: Computed values ​​can always be obtained from the current observable state using a pure function;
      2. Reactions: Reactions refer to the side effects that need to occur automatically when the state changes. In this case, we need to implement its read and write operations;
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'

Functional and Object Oriented

Redux follows the idea of ​​functional programming (FP) more, while Mobx considers problems more from an object-oriented perspective.

Redux advocates writing functional code, such as reducer is a pure function (pure function), as follows:

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

A pure function, which takes input and outputs a result, has no effect beyond that, including parameters that do not affect the received; always outputs the same result for the same input.

Mobx design is more inclined to object-oriented programming (OOP) and reactive programming (Reactive Programming), usually wrapping the state into observable objects, so we can use all the capabilities of observable objects, once the state object changes, it can automatically Get updates.

Single store and multiple stores

The store is where the application manages data. In Redux applications, we always centralize all shared application data in one big store, while Mobx usually divides the application 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 changes to all state objects;
  2. Observable objects can be monitored in Mobx, and the monitoring will be 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:
    retur nstate;
}

We cannot directly manipulate the state object, but always return a new state object based on the original state object, so that the previous state of the application can be easily returned; and Mobx can directly update the state object with the new value.

mobx-react和react-redux

When connecting with a Redux and React app, you need to use react-reduxthe provided Providersum connect:

  1. Provider: responsible for injecting the Store into the React application;
  2. connect: Responsible for injecting store state into container components, and select specific states to pass as container component props;

For Mobx, the same two steps are required:

  1. Provider: injects all stores into the application using mobx-reactthe provided ;Provider
  2. By injecting a injectspecific store into a component, the store can pass state or action; then using observerit to ensure that the component can respond to observable changes in the store, that is, store updates, and component views are updated responsively.

Reasons to choose Mobx

  1. Low learning cost: The basic knowledge of Mobx is very simple. After learning the official documentation and sample code for half an hour, a new project instance is built; while Redux is indeed more cumbersome and has many processes. It needs to configure, create a store, write a reducer, and action. Tasks, but also need to introduce redux-thunkor redux-sagawrite additional code, the Mobx process is much simpler, and does not require additional asynchronous processing libraries;
  2. Object-oriented programming: Mobx supports object-oriented programming. We can use @observableand @observerto make JavaScript objects responsive in object-oriented programming. Redux most recommends following functional programming. Of course, Mobx also supports functional programming;
  3. Less template code: Compared with various template codes of Redux, such as actionCreater, reducer, saga/thunk, etc., Mobx does not need to write such template code;

Possible reasons not to choose Mobx

  1. Too much freedom: Mobx provides few conventions and template codes, which makes the development code very free to write. If some conventions are not made, it is easy to cause the team code style to be inconsistent, so when there are many team members, it is really necessary to add some conventions;
  2. Scalable and maintainable: Maybe you are worried about whether Mobx can adapt to the development of later projects? It is true that Mobx is more suitable for use in small and medium-sized projects, but this does not mean that it cannot support large-scale projects. The key point is that large-scale projects usually require special attention to scalability and maintainability. In comparison, standardized Redux has more advantages. Mobx is more free, and we need to formulate some rules ourselves to ensure the later expansion of the project and the difficulty of maintenance;

code comparison

Next, we use Redux and Mobx to simply implement the same application, compare its code, and see how each performs.

Architecture

In a Redux application, we first need to configure, create the store, and use redux-thunkor redux-sagamiddleware to support asynchronous actions, and then Providerinject the store into the application using:

// 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')
);

Mobx applications can directly inject all stores into the application:

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')
);

Inject Props

Redux:

// 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);

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}
    />
  }
}

Define Action/Reducer, etc.

Redux:

// 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;
  }
}

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();

Asynchronous Action

If using Redux, we need to add redux-thunkor redux-sagasupport asynchronous actions, which requires additional configuration and writing template code, while Mobx does not require additional configuration.

The main code of redux-saga 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
];

some thoughts

No matter the front-end or the back-end, if you encounter problems, most of the time, maybe everyone is used to recommending the ones that have been widely promoted and used. It is easy to become familiar with habit and familiarity. We should think further, and the right one is better. of.

Of course, we should also avoid such inferences as "Redux is more standardized and reliable, you should use Redux" or "Redux templates are too many, too complicated, you should choose Mobx", these are relative terms, each framework has It has its own implementation, characteristics, and applicable scenarios. For example, the Redux process is more complicated, but after familiarizing itself with the process, you can better grasp some of its basic/core concepts, and you may have more experience and perception when using it; while Mobx is simplified, the Most things are hidden, and its core/basic ideas cannot be touched without special research, which may keep developers at the usage level.

So whether it's a technology stack or a framework. Class libraries, there is no absolute comparison, what should we choose and what should be discarded, we should pay more attention to what problems they solve, their focus on solving problems, or what is the implementation method, what are their advantages and disadvantages, which one It is more suitable for the current project and the future development of the project.

refer to

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324535056&siteId=291194637