ReactNativeとMobXを併存して動かすサンプルはそう多くなくあるが、ストアの注入とかのコードが理解しづらかったのと、react向けの記事では動かない部分もあったりするので最小限のコードでMobXを動かしてみるという方法をメモしておく。
MobXとはなにか
これは初心者の雑な理解。
ReactではUIパーツをコンポーネントという単位で開発する。コンポーネントはステートを持つことも可能だけれども、一般的な話だがステートを持つとテストしにくいとか、ステートの場所が散らばって管理が大変とかいう問題がある。
そこでその状態をもつ箇所を一箇所に集めてインターフェイスを統一する機構としてReduxという実装を用いるのがReactでは一般的になっている。
しかし、Reduxで実装するために書かなければならないコードが多く煩雑なため見通しもあまり良くなかったり、ボイラープレート的なものを書く必要が多かったりして覚えるのが大変だし使うのも面倒という点で初心者にはツラいと言われている気がする。
そこでもっと記述量を減らした便利なステート管理をするツールとしてMobXというのが使われる。しかし日本ではあまりMobXの採用事例がないっぽく、解説記事が英語の記事ばかりでそれはそれで初心者にはツラいなあみたいな状況がある気がする。
環境
react-native-cli: 2.0.1
react-native: 0.55.4
node 8.11.1
https://facebook.github.io/react-native/docs/getting-started.html
ここを参考に環境構築する。本筋ではないが、Building Projects with Native Code
の方で作成している(Quick Startだとネイティブ実装が必要でも書くことができないため)
以下、 react-native init ${プロジェクト名}
で作成したscaffold上での作業として記載している。
必要なパッケージをインストール
yarn add mobx # mobx本体
yarn add mobx-react # mobxをreact上で使うのを楽にしてくれるなにか
yarn add babel-preset-react-native-stage-0 # mobxのコードを書くときデコレーター(Javaでいうアノテーション的ななにか)を使用しているためこれを解釈するために必要
babelの設定を追加
{
"presets": ["react-native", "react-native-stage-0/decorator-support"]
}
特に何もしなければ presets
の中はreact-nativeしかないが、これにインストールした react-native-stage-0/decorator-support
を追加してデコレーターを使用可能にする。
Storeを作成する
import {
observable,
action
} from 'mobx'
class MyStore {
@observable num = 0;
@action.bound async incrementAsync() {
await new Promise((resolve) => {
setTimeout(() => {
this.num += 1;
resolve();
}, 1000)
});
}
}
const mofu = new MyStore()
export default mofu;
@observable
を付与した値はこの値が変更されたときに、これをobserveしているコンポーネントに変更が通知され、当該コンポーネントが再描画されるようになっている。
@action.bound
を使用するとobservableな値を加工する関数を書くことができる。今回はsetTimeoutとPromiseを使用した疑似非同期メソッドを作成したが、axiosなどで非同期にインターネット経由で値を書き込むといった使い方がユースケースとして想定される。
コンポーネント側で読み取る
import React, { Component } from 'react';
import {
Button,
Text,
View
} from 'react-native';
import { observer } from 'mobx-react/native'
import MyStore from './MyStore'
type Props = {};
@observer
export default class App extends Component<Props> {
render() {
return (
<View>
<Text>{MyStore.num.toString()}</Text>
<Button onPress={MyStore.incrementAsync} title="countUp"/>
</View>
);
}
}
@observer
はコンポーネント内で使用するストアに属するobservableな値の変更を検知したい場合に付与する。こうすると値が変更されたときに、ストアの値を使用しているコンポーネントがプロパティを置き換えて再描画される。
動作イメージ
初期状態で、Textはストアの値(num=0)を表示している。ボタンを押すとStoreのincrementAsyncメソッドが呼び出され、1秒後にnumの値が1増えた状態に書き換えられる。その変更がTextに通知され、Textの中の値が書き換わった状態で再描画されている。