LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログ

一部 React のサイトに MobX を導入しました

フロントエンジニアの田中です。 以下記事にもありますが、弊社のサービス:夜行バス比較なびの React 実装部分に MobX 導入しました。

techblog.lclco.com

目的

そもそもの導入目的は、親コンポーネントが違うコンポーネント同士の連携を行いたかったからです。 部分的に React を導入していたため、親が違うコンポーネント同士での連携ができていませんでした。

親コンポーネントが違うコンポーネント
f:id:lcl-engineer:20180614110008p:plain

やりたいこと
f:id:lcl-engineer:20180614110321p:plain

MobX 導入理由

MobX は Store を使用してクライアント側の状態(state)管理ができるフレームワークです。 Reactの状態管理フレームワークということで Redux が一番に候補として挙がりましたが、導入検討してみるとコードが複雑化(Store, Action, ActionCreater, Dispatcher, Reducer ...)してしまいました。そこで、簡略化するために MobX を導入しました。

Redux , MobX 比較

Redux

以下、Redux でのフローをまとめたものです。

  1. Store で state を定義
  2. Action Creater で Action を作成
  3. Action でデータ操作内容を定義
  4. Dispatch で Action を伝え state を変更
  5. Reducer で state 書き換え
  6. Provider(react-redux)でreactコンポーネントに連携

f:id:lcl-engineer:20180614130440p:plain 参考:Redux入門【ダイジェスト版】10分で理解するReduxの基礎

MobX

以下、MobX でのフローをまとめたものです。

  1. Store で state, action を定義
  2. Actionでデータ操作し、自動的に state 更新
  3. Computed valuesで使用するデータを操作
  4. Reactionという機能が自動でreactコンポーネントに連携

f:id:lcl-engineer:20180614130448p:plain 参考:MobX - Introduction

上記を比べてみても MobX での実装フローが少なく済むことが分かります。MobX では、主に dispatch と reducer の内容が隠蔽されている印象です。 詳しい説明や概要図は引用元に記載されているので、確認してみてください。

MobX 導入サンプルコード

以下コードはサンプルです。
既存のコードをMobXを使って書き換えました。 今回はあくまで親コンポーネントが違うコンポーネントの状態管理が目的のため、関連部分のみを書き換えています。 (MobXの機能を活用すればもっと良い書き方があるかもしれません)

■ Store.jsx(Storeの定義)

import { observable, action } from 'mobx';
import ClipController from ‘../component/clip/ClipController';

class ClipStore {
  // stateを初期定義
  @observable clip_count = ClipController.getClipCount();

  // actionの定義
  @action
  changeClipCount(count) {
    this.clip_count = count;
  }
}
export default ClipStore;


■ index.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import Clip from ‘./component/clip/Clip.jsx';
import HeaderIconClip from ‘./component/clip/HeaderIconClip.jsx';
import ClipStore from './ClipStore.jsx';

// storeのインスタンス化
const clipStore = new ClipStore();

ReactDOM.render(
  // コンポーネントへ受け渡し
  <HeaderIconClip clipStore={clipStore}/>,
  document.getElementById('header-button-clip')
);
ReactDOM.render(
  // コンポーネントへ受け渡し
  <Clip clipStore={clipStore}/>,
  document.getElementById('clip-page')
);


■ Clip.jsx

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

(...省略)

// MobXのstore を Reactコンポーネントで使用するための定義
@observer
class Clip extends React.Component {
  removeClip(record) {
  (...省略)
    this.props.clipStore.changeClipCount(this.props.clipStore.clip_count - 1);
  } 

  render() {
    return (
      <div>
        (…省略)
        <button onClick={() => {removeClip(record)}>
    )
  }
}

export default Clip;


■ headerIconClip.jsx

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

// MobXのstore を Reactコンポーネントで使用するための定義
@observer
class HeaderIconClip extends React.Component {
  render() {
    return (
      (…省略)
        <span>
          {this.props.clipStore.clip_count}
        </span>
    );
  }
}

export default HeaderIconClip;

MobXのブラウザ対応状況

MobX 5 ではProxy object を使用しており、サポートしていないブラウザでは動作せずにエラーが出てしまいました(実際に iOS9 や Android6 以下の端末でエラーが出ていたことを確認しています)。 そのため、MobX 5 を導入しようとしていましたが、MobX 4 に変更をしました。

まとめ

元々の目的を達成するには Redux , MobX のどちらでも良かったですが、MobX を導入することで以下のメリットが得られました。

  • dispatch, reducer 記述がなくて済む分コード量が少なくて済む
  • 他エンジニアの学習コストが減らせる

日本語情報が少ない気はしますが、MobX の公式ドキュメント(英語)は充実しています。 とにかく状態管理フレームワークを導入した開発を行いたいという場合に扱いやすいと思います。

参考