Use observables instead of state in React components

Example of maintaining state with a simple observable in your own component:

import React from 'react';
import { observable } from 'mobx';
import { observer } from 'mobx-react';

@observer class MyComponent extends React.Component {
  onClick = () => {
    this.active = true;
    // ...
  };

  @observable active = false;

  render() {
    return (
      <a onClick={this.onClick} disabled={this.active}>click works only once</a>
    );
  }
}

Note that you are not passing an observable to the "a" component, you are passing a plain value (in this case a Boolean). When the observable changes, the render method above will be re-evaluated by React.

For components high in the component/DOM tree, re-evaluating their render method and all descendents' render methods can be expensive. It is therefore better to pass observable objects to child components, i.e. "dereference lately" as recommended on https://mobx.js.org/best/react-performance.html

Note that in non-production mode, this performance optimization will not occur when components declare propTypes, but it will work correctly in React production mode. See https://github.com/mobxjs/mobx-react/issues/56

Passing objects with observable properties also makes it easy to control the state of a child component both from within itself and from its parent. A common use case is a popup/overlay component that its parent needs to open and the overlay itself needs to be able to close:

@observer class ParentComponent extends React.Component {
  onOpenClick = () => {
    this.overlayState.active = true;
  };

  @observable overlayState = {
    active: false,
  };

  render() {
    return (
      <div>
        <a onClick={this.onOpenClick}>open</a>
        <Overlay observable={this.overlayState}>
      </div>
    );
  }
}

@observer class Overlay extends React.Component {
  onCloseClick = () => {
    this.props.observable.active = false;
  };

  render() {
    return (
      {this.props.observable.active ?
        <div>
          <a onClick={this.onCloseClick}>close</a>
        </div>
      : null}
    );
  }
}

Overlay.propTypes = {
  observable: React.PropTypes.shape({
    active: React.PropTypes.bool.isRequired,
  }).isRequired,
};

Note that ParentComponent's render method will not be re-evaluated when the active boolean is toggled, only Overlay's.

@action decorator in a React Component will break react-hot-loader

If, in your React component, you want to have a method that will update more than one observable, to make this atomic (and reduce re-renders), wrap the updates in an action() but do not use the @action decorator as currently this will cause an error in react-hot-loader (even if you edit and need to hot-reload a different component from the one with the decorator), i.e.:

@observer class ParentComponent extends React.Component {
  myMethod = () => {
    action(() => {
      this.observable1 = true;
      this.observable2 = true;
    })();
  };

  @observable observable1 = false;
  @observable observable2 = false;
...
}

For more on this issue see:

Last updated