フロントエンジニアの田中です。 以下記事にもありますが、弊社のサービス:夜行バス比較なびの 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 の公式ドキュメント(英語)は充実しています。 とにかく状態管理フレームワークを導入した開発を行いたいという場合に扱いやすいと思います。