MobX with React Introduction

By Amitai B.

Mar 2, 2018

MobX is a simple state management solution that can be used very easily for ReactJS.

ReactJS contains a JavaScript library for building user interfaces, but it doesn’t have a built-in state management or two-way binding like Angular does (in version 16.3 it has changed with the introduction of a new Context API).

In the last couple of years, Redux has established itself as the go-to solution for state management.

In this blog post, I will introduce MobX which is a much easier and cleaner alternative to Redux. I will not compare the two, but only say that MobX is very common.

MobX principals:

Actions -> State -> Derivations + Reactions

  • State – the state of the application (similar to Redux’s store).
  • Derivations – value that can be computed automatically from the state.
  • Reactions – allows you to automatically perform some task like changing the DOM when the state changes.
  • Actions – functions that change the state.

mobx-react – a library that binds mobx with react.

MobX decorators

Mobx (and mobx-react) can be used with regular JS functions or with decorators. Decorators make for a much cleaner solution so I will use them in this post.
Mobx decorators:

  • @observable – This turns a property into something observable. Observers will be notified and react to changes in those properties. The properties can be objects, arrays or references.
  • @computed – values that will be derived automatically when relevant data is modified.
  • @observer (mobx-react) – This makes a react component responsive to the state change. Basically, it calls the component’s render function when the state changes.
  • @action – This involves a method that changes the state.
  • Provider and @inject – Allows you to inject the store into the component (like connect in Redux).

MobX + React example

In this section, I will demonstrate how to use mobx with react. I will create a grocery application that allows you to add or remove groceries from a list and count their total.

I will use the create-react-app for bootstrapping the application, and then add mobx and mobx-react:

npx create-react-app groceries
cd groceries
npm -i --save mobx mobx-react
npm run eject
npm install --save-dev babel-plugin-transform-decorators-legacy babel-plugin-transform-class-propertie

I eject the create-react-app in order to use decorators (create-react-app doesn’t support decorators).

First, I will create a grocery store:

// groceries.store.js
import { observable, computed, action } from "mobx";


export default class GroceryStore {
    @observable groceries = [];

    @action
    add(g) {
        this.groceries.push(g);
    }

    @action
    delete(name) {
        this.groceries.remove(name)
    }

    @computed 
    get numOfGroceries() {
        return this.groceries.length;
    }
}

The store has one observable (groceries), two actions (add and delete) and one computed (numOfGroceries).

The application UI has two parts: an “add” component to add groceries and the list of groceries.

// App.js

import React, { Component } from 'react';
import { Provider } from 'mobx-react';
import {  } from 'mobx-react';

import './App.css';
import GroceryStore from './groceries.store';
import Add from './add.component';
import GroceriesList from './groceriesList.component';

const groceryStore = new GroceryStore();

class App extends Component {

  render() {
    return (
      <Provider groceryStore = {groceryStore}>
        <div className="App">
          <Add />
          <GroceriesList />
        </div>
      </Provider>
    );
  }
}

export default App;

I create the grocery store and use provide to transfer it to the children.

The add component:

// add.component.js

import React, { Component } from 'react';
import { inject } from 'mobx-react';


import './App.css';

@inject("groceryStore")
class Add extends Component {
  constructor(props) {
    super();
    this.state = {
      inputValue: ''
    }
  }

  updateInputValue = (evt) => {
    this.setState({
      inputValue: evt.target.value
    });
  }

  add = () => {
    this.props.groceryStore.add(this.state.inputValue)
    this.setState({
      inputValue: ''
    });
  }
  render() {
    return (
      <div className="App">
        <div className="add-grocery">
          <input placeholder="Add new" type="text" value={this.state.inputValue} onChange={evt => this.updateInputValue(evt)}/>
          <button onClick={this.add}>Add</button>
        </div>
      </div>
    );
  }
}

export default Add;

The Add component uses mobx store to perform an action (add). I use @inject to add it to the component.

The groceries list:

// groceriesList.component.js

import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';

import './App.css';
import Grocery from './grocery.component';

@inject("groceryStore")
@observer
class GroceriesList extends Component {

  render() {
    const store = this.props.groceryStore;
    return (
        <div>
            <h2>Total: {store.numOfGroceries}</h2>
            <div className="list">
                {store.groceries.map((g,index) =>  <Grocery key={index} name={g}/> )}
            </div>
        </div>

    );
  }
}

export default GroceriesList;

Here we use the @observer decorator for the first time. We would like the component to re-render when the state has changed.

The grocery component:

// grocery.component.js

import React, { Component } from 'react';
import { inject } from 'mobx-react';

import './App.css';

@inject("groceryStore")
class Grocery extends Component {

 delete = () => {
    this.props.groceryStore.delete(this.props.name)
 }
  render() {
    return (
        <li className="grocery" onClick={this.delete} key={this.props.index}>{this.props.name}</li>
    );
  }
}

export default Grocery;

Again, we inject the store and use the delete action.

That’s it!

Conclusion

MobX is very elegant, although it is not a flux implementation. It lets you work with react without any boilerplate code like actions, reducers, or middlewares.

Give it a try!

References

  • https://mobx.js.org/
  • https://mobx.js.org/getting-started.html
  • https://github.com/mobxjs/mobx-react
  • https://github.com/mobxjs/mobx

Leave a Reply

Your email address will not be published.