フロントエンジニアの田中です。 以下記事にもありますが、弊社のサービス:夜行バス比較なびの React 実装部分に MobX 導入しました。
目的
そもそもの導入目的は、親コンポーネントが違うコンポーネント同士の連携を行いたかったからです。 部分的に React を導入していたため、親が違うコンポーネント同士での連携ができていませんでした。
親コンポーネントが違うコンポーネント
やりたいこと
MobX 導入理由
MobX は Store を使用してクライアント側の状態(state)管理ができるフレームワークです。 Reactの状態管理フレームワークということで Redux が一番に候補として挙がりましたが、導入検討してみるとコードが複雑化(Store, Action, ActionCreater, Dispatcher, Reducer ...)してしまいました。そこで、簡略化するために MobX を導入しました。
Redux , MobX 比較
Redux
以下、Redux でのフローをまとめたものです。
- Store で state を定義
- Action Creater で Action を作成
- Action でデータ操作内容を定義
- Dispatch で Action を伝え state を変更
- Reducer で state 書き換え
- Provider(react-redux)でreactコンポーネントに連携
参考:Redux入門【ダイジェスト版】10分で理解するReduxの基礎
MobX
以下、MobX でのフローをまとめたものです。
- Store で state, action を定義
- Actionでデータ操作し、自動的に state 更新
- Computed valuesで使用するデータを操作
- Reactionという機能が自動でreactコンポーネントに連携
上記を比べてみても 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 の公式ドキュメント(英語)は充実しています。 とにかく状態管理フレームワークを導入した開発を行いたいという場合に扱いやすいと思います。