MobXを使ったリアクティブなReact開発の実践ガイド

## MobX とは

MobX は JavaScript の状態管理ライブラリで、React などのフレームワークと組み合わせて使用することが多く、特徴は状態管理をシンプルで直感的に行うことができる点である。以下の 3 つの主要な概念に基づいている。

1. State
   アプリケーションを動かすデータのこと。スプレッドシートのセルのように扱うことで、その変更を追跡する。
2. Actions
   State を変更するコード。ユーザーイベント、バックエンドデータへの送信、スケジュールされたイベントなどが該当します。
3. Derivations
   State から相互作用なしに導き出すことができるもの。ユーザーインターフェース、導出データ（残りの todo の数など）、バックエンドの統合（サーバーへの変更の送信など）が該当します。

## 原則

MobX は、action が state を変更し、それによって影響を受けるすべてのビューが更新されるという一方向のデータフローを使用する。
![MobX Flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3868060/83aef663-bff3-c52e-aed2-928ebcb13a18.png)


1. すべての*derivation*は、state が変化すると自動的に更新される。
2. デフォルトではすべての*derivation*は同期的に更新される。例えば state を変更した後に、action が computed value を直接安全にみれることを意味する。
3. computed values 遅延的に更新される。アクティブに使用されていない computed value は、副作用（I/O）で必要になるまで更新されない。
4. すべての computed value は純粋でなければなく、computed value は state を変更することはない。

## 使い方

### 1. State を定義して観測可能にする

プリミティブやオブジェクトや配列など好きなデータを State にできる。変化させたい要素を MobX が観測できるようにするために`observable` として設定する。

```tsx
import { makeObservable, observable, action } from "mobx";

class Todo {
  id = Math.random();
  title = "";
  finished = false;

  constructor(title: string) {
    makeObservable(this, {
      title: observable, // state
      finished: observable, // state
      toggle: action, // action
    });
    this.title = title;
  }

  // actionでstateを変更している
  toggle() {
    this.finished = !this.finished;
  }
}
```

### 2. Actions を使って状態を更新する

Actions とは、State を変更するコードの一部であり、ユーザーイベント、バックエンドへのデータプッシュ、スケジュールされたイベントなどのこと。Actions は、ユーザーがスプレッドシートのセルに新しい値を入力するようなものである。

上の`Todo`クラスでは、`finished`の値を変更する`toggle`メソッドが action である。`observable`を変更するコードはすべて`action`としてマークすることを推奨。そうすることで、MobX は自動的にトランザクションを適用し、最適なパフォーマンスを得ることができる。actions を使用することで、コードを構造化することができ、意図しないときに誤って状態を変更してしまうことを防ぐことができる。

### 3. State の変化に自動的に対応する Derivations を作成する

State から導出されるものはすべて Derivations である。Derivations は 2 種類に分けられる。

1. Computed values
   observable の State から計算して導出される値。観測している State が変更されると自動的に再計算される。
2. Reactions
   State が変更されたときに自動的に発出する必要がある動作、副作用。

MobX を使い始めると Reactions を使いがちになるが、現在の State に基づいて値を作成したい場合は、常に computed values を使用すること。

#### 3.1 Computed values の使い方

computed values を作成するには、getter 関数を使用してプロパティを定義し、makeObservable で computed としてマークする。

```tsx
import { makeObservable, observable, computed } from "mobx";

class TodoList {
  todos: Todo[] = [];

  constructor(todos: Todo[]) {
    makeObservable(this, {
      todos: observable, // state
      unfinishedTodoCount: computed, // computed value
    });
    this.todos = todos;
  }

  // stateを使用して新たな値を計算している = computed value
  get unfinishedTodoCount() {
    return this.todos.filter((todo) => !todo.finished).length;
  }
}
```

todo が追加されたときや`finished`プロパティが変更されたときに、`unfinishedTodoCount`が自動的に更新される。

スプレッドシートの数式みたいなもの。自動的に更新されるが、必要なときだけ更新がされる。

#### 3.2 Reactions の使い方

ユーザが画面上で State や Computed values の変化を見ることができるようにするには、GUI の一部を再描画する reaction が必要。

reaction は computed values と似ているが、情報を生成する代わりに、console.log への出力、ネットワークリクエスト、React コンポーネントツリーの増分更新などの副作用を生成する。

最もよく使われる reaction は UI コンポーネント。action と reaction の両方から副作用を引き起こすことが可能であることに注意。フォーム送信時のネットワークリクエストのような、トリガー元が明確で明示的な副作用は、関連するイベントハンドラから明示的にトリガーされるべき。

#### 3.3 リアクティブな React components

React の場合、`Observer`タグを使うすることでコンポーネントをリアクティブにすることができる。`Observer`タグは子要素を関数として受け取り、その関数の戻り値をレンダリングする。関数内の State が変更されると、関数が再評価され、結果が再レンダリングされる。

```tsx
"use client";

import { Observer } from "mobx-react-lite";

const todoList = new TodoList([
  new Todo("Get coffee"),
  new Todo("Write simpler code"),
]);

const TodoView = ({ todo }: { todo: Todo }) => (
  <Observer>
    {() => (
      <li>
        <input
          type="checkbox"
          checked={todo.finished}
          onClick={() => todo.toggle()}
        />
        {todo.title}
      </li>
    )}
  </Observer>
);

const TodoListView = () => {
  return (
    <Observer>
      {() => (
        <div>
          <ul>
            {todoList.todos.map((todo) => (
              <Observer key={todo.id}>
                {() => <TodoView todo={todo} />}
              </Observer>
            ))}
          </ul>
          Tasks left: {todoList.unfinishedTodoCount}
        </div>
      )}
    </Observer>
  );
};

export default TodoListView;
```

上記の例の onClick ハンドラは、`toggle`action を使用しているため、`TodoView`コンポーネントを再レンダリングさせるが、未完了タスクの数が変化した場合にのみ`TodoListView`コンポーネントを再レンダリングさせる。`Tasks left`の行を削除すれば（または別のコンポーネントに入れれば）、タスクにチェックを入れても`TodoListView`コンポーネントは再レンダリングされなくなる。

## 参考文献

[MobX 公式サイト](https://mobx.js.org/README.html)