nextjs + TypeScript + Mobxの環境構築メモ

最近いまさらMobxを触り始めて、控えめに言って最高だったので、`nextjs` + `TypeScript` + `mobx` の環境構築方法をメモしておく、TypeScriptとの相性も良いし、reducerだの書かなくてよいし、Vueで使えるならそっちでも使いたいと思えるぐらい良かった。

まだ触り始めたばっかりなので、これから色々拡張していく気がするけど、とりあえず最低限動かせるところまでメモしておく

## 構築

#### nextjs + TypeScript環境の構築

[公式サイト]
(https://github.com/zeit/next.js/tree/canary/examples/with-typescript)にexampleがあるので、簡単のためそれを使う

```
$ npx create-next-app --example with-typescript my-app
```

#### mobxの導入

```
$ npm install mobx mobx-react
```

現時点(2019/08/12)では`@babel/plugin-proposal-class-properties` と `@babel/plugin-proposal-decorators` を入れないと、`nextjs` 環境で `mobx`のデコレータをうまく使えないので以下をインストール

```
$ npm install --save-dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators
```

さらに`.babelrc`を以下のように追加する

```
$ touch .babelrc
```

```/.babelrc
{
    "presets": [
        "next/babel"
    ],
    "plugins": [
        [
            "@babel/plugin-proposal-decorators",
            {
                "legacy": true
            }
        ],
        [
            "@babel/plugin-proposal-class-properties",
            {
                "loose": true
            }
        ]
    ]
}
```

また、TypeScript環境だと、`experimentalDecorators`オプションを有効化しないと警告が出るので、tsconfigを以下のように書き換える

```tsconfig.json
{
  "compilerOptions": {
    ... 略
    "experimentalDecorators": true,   // <= 追加
     ....略
  },
}


```

## 動作確認

#### Storeの作成

まだ触り始めたばっかりなので、mobxでどういうstore設計にするのが良いのかはよくわかっていない。
とりあえず現時点で動いているものをメモする

とりあえずStoreを作る。プロジェクト直下に`store`ディレクトリを作り、以下の２つのファイルを作る

今回作るのはチュートリアルでよく使われるカウンターアプリ、ストアの中には、`counter`という変数と、そのカウンターを増加させる`increment()`関数をとりあえず作る

```./store/CounterStore.ts
import { observable, action } from 'mobx'

export class CounterStore{
    @observable counter = 0

    @action
    increment(){
        this.counter++
    }
}
```

`@observable`というデコレータがクラス変数や関数につけられていることがわかる。`@observable`を付与された変数はmobxの変更監視対象となり、この値を参照しているコンポーネントがあれば、値の変更に応じて、適切にコンポーネントを再レンダリングしてくれるようになる。

加えて、Storeのインスタンスを保持する`store.ts`を作る。ここで作成されたStoreをコンポーネントから呼び出すことで、Storeの状態や関数にアクセスできる

```./store/store.ts
import { CounterStore } from "./CounterStore";
import { configure } from "mobx";

// actionを経由せずに@observableをつけたプロパティを更新しようとするとエラーになるように設定
configure({ enforceActions: 'always' });

export const counterStore = new CounterStore()
```

ストアの記述はこれだけ、とても簡単。実際のところmobxはStoreを作るためのライブラリというよりは、単に「JSインスタンスの変数の変更をコンポーネント側に通知するライブラリ」って感じっぽい。
この「一つのことをうまくやる」感じはとても良い。Reduxはヘビーすぎるねん・・・

#### ページコンポーネントの作成

`page`ディレクトリの直下に以下のファイルを作ってStoreがうまく使えるか試してみる

```counter.tsx
import * as React from 'react'
import Layout from '../components/Layout'
import { counterStore } from '../store/store';
import { observer } from 'mobx-react'

@observer
class CounterPage extends React.Component {

    render() {
        return (
            <Layout>
                <p>counter: {counterStore.counter}</p>
                <button onClick={() => counterStore.increment()}>+</button>
            </Layout>
        )
    }
}

export default CounterPage
```

`@observer` というデコレータをclassコンポーネントの前につけると、このコンポーネントがStoreの中身の変更を監視してくれるようになる。

最終的なフォルダ構成は以下

```
├── pages
│   ├── about.tsx
│   ├── counter.tsx    // 追加
│   ├── detail.tsx
│   ├── index.tsx
│   └── initial-props.tsx
├── store
│   ├── CounterStore.ts   // 追加
│   └── store.ts          // 追加
```

ここまでできたら`npm run dev` を実行し、以下のURLにアクセスして、今回作ったコンポーネントがうまく動くかテストする

http://localhost:3000/counter

+ボタンをポチポチしてカウンターが増えていればOK

以上。

### 余談

公式サイトにnextjs + mobxのサンプルも落ちており、そちらだと、`_app.js`の中でstoreのインスタンスを持ち、`@inject` を使って、propsにstoreインスタンスを差し込むような設計になっている。
propsが汚れそうなのがちょっと気持ち悪いけど、こっちのほうが良いのだろうか？
https://github.com/zeit/next.js/tree/canary/examples/with-mobx