【React  / React Native】ReduxからMST（mobx-state-tree）へ

## MSTの概要
ポストReduxとして注目を浴びているのが、Mobxの拡張であるMST（Mobx-state-tree）です。Mobxはシンプルで扱いやすい一方で、いろいろな問題が起きやすいため、タイプ情報を定義することによって、それらを極めてラディカルに解決しようというのがこのMSTになります。
今回はMobX-State-Treeの基本的な仕組みと書き方をメモします。

## MSTとは何か

### Mobx単体との違い

主な参考記事：
https://medium.com/react-native-training/state-management-with-mobx-state-tree-373f9f2dc68a

何が違うか:

- Opinionated（独善的）: データがどのように構造化されるべきか、または更新されるべきかがルール化されている。
- Typed — 独自のtypes APIがある
- Models — 複数モデルのツリーが生成される

observable（観察可能）であること、mutable（高可変性）であることは同じです。

### 哲学
公式サイトより翻訳（意訳あり）
https://mobx-state-tree.js.org/intro/philosophy

>**MST（mobx-state-tree）**は、 可変データの「シンプルさ」と「扱いやすさ」、不変データの「Reactivity」、「トレーサビリティ」、そして「観測可能データのパフォーマンス」を備えた**Stateのコンテナ（container）**です。

>簡単にいうと、MSTは、状態管理に対する**不変性**（取引のしやすさ、トレーサビリティ、構成）と**可変性**（検出のしやすさ、配列法、カプセル化）の**両方のアプローチの最良の特徴を組み合わせること**を目的としています。

>Mobx単体とは違って、MSTは**データをどのように構造化し、更新すべきか**を**独自のルール**に従って決めることで、Mobxでよく起こる問題をすぐに解決できます。

>MSTの中心にあるのは、**生きた木（ツリー）**という概念です。このツリーは、**変更可能ですが厳密に保護されたオブジェクトで構成され、実行時タイプ情報が付加**されます。つまり、各ツリーには**形状(タイプ情報)**と**状態(データ)**があります。

>この生きた木から、**普遍的な、共通のスナップショット**が自動的に生成されます。


## サンプルコード
以下は、公式サイトにある簡易的なサンプルコードを少し修正したものです。

```javascript:MSTのサンプルコード
import { types, onSnapshot } from "mobx-state-tree"

// typesを使ってmodel構造とactionsを定義
const Todo = types
    .model("Todo", {
        title: types.string,
        subTitle: types.string,
        done: false
    })
    .views(self => ({
        fullTitle() {
            return self.title + self.subTitle
        }
    }))
    .actions(self => ({
        toggle() {
            self.done = !self.done
        }
    }))

// StoreでTodoモデルをラップ
const Store = types.model("Store", {
    todos: types.array(Todo)
})

// Storeからインスタンスを作成
const store = Store.create({
    todos: [
        {
            title: "Get coffee"
        }
    ]
})

// 新しいsnapshotの作成をlisten
onSnapshot(store, snapshot => {
    console.dir(snapshot)
})

// toggleアクションを呼んでみる
store.todos[0].toggle()

// doneのtrue/falseが切り替わる
// onSnapshotが発火し、コンソールログに`{ todos: [{ title: "Get coffee", done: true }]}`が表示される

// viewsからフルタイトルを取得する
store.todos[0].title()
```

modelファイルはmodels/配下に置くのが一般的なようです。

Reduxに比べてめちゃくちゃシンプルですが、グローバルなState/Action管理という目的では特に過不足ないかと思います。

## Example
公式でExampleが用意されています。

https://mobx-state-tree.js.org/intro/examples

## オブジェクトのネストと配列
例えば、以下のようなネストされたオブジェクトや配列を管理したい場合について解説します。

```json
{
    "id": 1,
    "location": {
        "latitude": 135.22313,
        "longitude":  41.2335,
    },
    "tags": [{name: "Male"}, {name: "Premium"},{name: "Approved"}],
	"addresses": [
		{
			"id": 2,
			"zip_code": "1234567",
			"prefectures": "東京都",
			"city": "渋谷区",
			"street_address": "渋谷",
			"street_address_optional": "1-23-4",
		}
	]
}
```

オブジェクトの形式のデータをネストする場合は、modelを作成してそれをネストします。

```javascript
const locationModel = types.model({
  latitude: types.number,
  selected: types.number
});

const userModel = types.model({
  id: 1,
  location: locationModel,
  ...
});
```

配列の場合は、types.arrayを使用します。


```
const tagModel = types.model({
  name: types.string
});

const userModel = types.model({
  id: 1,
  tags: types.array(tagModel),
  ...
});
```

## Viewとの組み合わせ方
observeはmobx-reactで提供されているモジュールを使います。

```jsx
import { observer } from "mobx-react";

const App = observer(props => (
    <div>
        <button onClick={e => props.store.addTodo(randomId(), "New Task")}>Add Task</button>
        {values(props.store.todos).map(todo => (
            <div>
                <input type="checkbox" checked={todo.done} onChange={e => todo.toggle()} />
                <input type="text" value={todo.name} onChange={e => todo.setName(e.target.value)} />
            </div>
        ))}
    </div>
))
```

## まとめ
自社のReact Nativeアプリで導入しましたが特に大きな問題はなさそうです。
Reduxのように洗練されたベストプラクティスがないため、Storeの設計などには苦労しそうです。