LoginSignup
7
4

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-08-12

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

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

構築

nextjs + 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ディレクトリを作り、以下の2つのファイルを作る

今回作るのはチュートリアルでよく使われるカウンターアプリ、ストアの中には、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にアクセスして、今回作ったコンポーネントがうまく動くかテストする

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

以上。

余談

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

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4