React

MobXとは?Reactの状態管理をシンプルにする仕組みと使い方【v6対応】

MobXは、状態(データ)の変化を自動で追跡し、その状態を使っている画面だけを再描画するReact向けの状態管理ライブラリです。「状態を変えたらUIが勝手に追従する」という考え方で、Reduxのような定型コードを書かずに状態管理を実装できます。本記事のMobXは、JavaScript/TypeScript向けライブラリ(mobx.js.org)を指します。米国市場に上場するMobix Labs(ティッカー「MOBX」)とは無関係です。ここではMobX 6を前提に、中核となる考え方、Reactとの連携、そしてReduxやZustandとの使い分けまでを実コードで整理します。

まとめ:MobXの要点

  • MobXはObservableにした状態の変化を追跡し、依存するUIだけを自動で再描画するリアクティブな状態管理ライブラリ。
  • 中核はObservable(状態)・Action(変更)・Computed(派生値)の3つ。副作用はReaction(autorun/reaction)で扱う。
  • MobX 6ではデコレータが既定でなくなり、makeAutoObservableでストアを定義するのが標準の書き方。
  • Reactとはmobx-react-liteobserverで連携する。クラスコンポーネントやProvider/injectが必要なときだけmobx-reactを使う。
  • 記述量は少ないが規模が大きいほど設計規律が要る。厳格な監査可能性ならRedux、最小構成ならZustandと使い分ける。

以降で、それぞれの仕組みと具体的な書き方を順に見ていきます。

MobXの全体像:リアクティブな状態管理の考え方

MobXは2015年にMichel Weststrate氏が「Mobservable」として公開し、翌年MobXへ改称したライブラリです。設計思想は「派生できるものはすべて自動的に派生させる」というもので、状態を書き換えたら、その状態に依存する計算値や画面が自動的に更新されます。開発者が「どこを再描画するか」を手で指定する必要がありません。

Reduxが「状態は一箇所・変更はアクションとリデューサー経由」という明示的な規約でボイラープレートが増えがちなのに対し、MobXは通常のオブジェクトのプロパティを書き換える感覚でコードを書けます。この差が、MobXが「記述量が少ない」と評価される理由です。半面、どこでも状態を変えられるため、規模が大きくなると変更箇所を追いにくくなる弱点もあります(後述のActionと使い分けで抑えます)。

MobXの3つの中核概念:Observable・Action・Computed

MobXの仕組みは、状態を表すObservable、状態を変えるAction、状態から値を導くComputedの3つで説明できます。この3つに、状態変化に反応して副作用を起こすReactionを加えた4つがAPIの土台です。

Observable:追跡対象になる状態

Observableは「変化を監視される状態」です。オブジェクト・配列・Map/Set・プリミティブを対象にできます。あるコンポーネントがObservableのどのプロパティを読んだかをMobXが記録し、そのプロパティが変わったときだけ該当コンポーネントを再描画します。「使っている値だけを追跡する」ため、無関係な再描画が起きにくいのが特徴です。

Action:状態を変更する窓口

Actionは状態を書き換えるメソッドです。MobXでは変更をActionに集約することが推奨され、MobX 6は既定でstrict mode(enforceActions: "observed")が働きます。リアクションが参照している状態をAction外で書き換えるとエラーになります。変更箇所をActionに限定すると、状態がいつ・どこで変わったかを追いやすくなり、大規模化したときのデバッグ性が上がります。makeAutoObservableを使えば、メソッドは自動的にActionとして扱われます。

Computed:依存から導かれるキャッシュ付きの派生値

Computedは、既存のObservableから計算される値です。ゲッターとして定義し、依存するObservableが変わったときだけ再計算されます。結果はキャッシュされ、誰も参照していない間は計算されないため、同じ導出をUIの複数箇所で使っても無駄な計算が走りません。「合計金額」「未完了件数」のような、状態から一意に決まる値はComputedに寄せるのが定石です。

Reaction:状態変化に応じた副作用

ComputedがUIに表示する「値」を作るのに対し、Reactionは「副作用」を起こします。autorunは依存する状態が変わるたびに関数を再実行し、reactionは監視対象と副作用を分けて書けます。ローカルストレージへの保存やログ出力など、画面描画以外の反応に使います。Reactコンポーネントの再描画自体は、後述のobserverが内部でReactionを張って実現しています。

MobX 6の書き方:makeAutoObservableとデコレータの位置づけ

ここが古い解説記事と最も差が出るところです。MobX 4/5時代はデコレータ(@observable@action)が事実上の標準でしたが、MobX 6ではデコレータが既定ではなくなり、コンストラクタでmakeObservablemakeAutoObservableを呼ぶ方式が標準になりました。ビルド設定なしでどの環境でも動く形に統一するための変更です。

makeAutoObservableでストアを定義する(推奨)

makeAutoObservableは、プロパティを自動でObservable、ゲッターをComputed、メソッドをActionとして推論します。注釈を1つずつ書かずに済むため、公式ドキュメントでもまずこの書き方が示されています。

import { makeAutoObservable } from "mobx"

class CounterStore {
  count = 0

  constructor() {
    makeAutoObservable(this)
  }

  increment() {
    this.count++
  }

  get isEven() {
    return this.count % 2 === 0
  }
}

export const counterStore = new CounterStore()

この例ではcountがObservable、incrementがAction、isEvenがComputedに自動で振り分けられます。旧記事にあったobservable({ count: 0, increment() {...} })というオブジェクト直書きも動きますが、クラス+makeAutoObservableのほうがTypeScriptとの相性がよく、現在の主流です。

makeObservableで注釈を明示する場合

自動推論を効かせたくない、あるいは一部だけ扱いを変えたいときはmakeObservableで明示します。第2引数でobservableactioncomputedを指定します。継承したクラスやgetterの扱いを細かく制御したい場面で使い分けます。

デコレータを使う場合の注意

デコレータ構文自体はMobX 6でも「modern decorators(@observable accessor)」として利用できますが、既定ではありません。既存のMobX 4/5プロジェクトからの移行では、デコレータを外してmakeObservable(this, {...})に置き換えるのが公式の移行手順です。新規開発でわざわざデコレータ設定を足す必要は基本的にありません。

MobXとReactの連携:mobx-react-liteとmobx-reactの選び方

MobX本体はReactに依存しません。Reactとつなぐにはバインディングパッケージを入れ、コンポーネントをobserverでラップします。ここでmobx-react-litemobx-reactのどちらを選ぶかが最初の分岐点です。

インストールとセットアップ

関数コンポーネント中心の現在の開発では、本体と軽量バインディングを入れます。

npm install mobx mobx-react-lite

本記事執筆時点の最新は mobx 6.16/mobx-react-lite 4.1 系です(パッチは更新が速いため、最新は公式・npmで確認してください)。クラスコンポーネントやProvider/injectも使いたい場合は、mobx-react-liteの代わりにmobx-react(9系)を入れます。

observerでコンポーネントをリアクティブにする

observerでラップしたコンポーネントは、レンダリング中に読んだObservableを自動で追跡し、その値が変わったときだけ再描画されます。

import { observer } from "mobx-react-lite"
import { counterStore } from "../stores/counterStore"

const Counter = observer(() => (
  <div>
    <p>{counterStore.count}({counterStore.isEven ? "偶数" : "奇数"})</p>
    <button onClick={() => counterStore.increment()}>+1</button>
  </div>
))

export default Counter

useStateを使わずとも、ストアのcountを書き換えるだけで表示が更新されます。ストアはコンポーネント外に置くか、React Contextで配布します。

mobx-react-liteとmobx-reactの使い分け

両者の違いを整理すると、選択基準は明確です。

項目 mobx-react-lite mobx-react
対応コンポーネント 関数のみ 関数+クラス
Provider/inject なし(useContextで代替) あり
サイズ 約1.5kB(gzip)と軽量 lite+クラス対応分
推奨用途 新規・関数コンポーネント中心 クラスや旧来APIが残る場合

結論として、新規のReactプロジェクトならmobx-react-liteで十分です。mobx-reactは内部でmobx-react-liteを使っており、クラスコンポーネントやProvider/injectという追加機能が要るときだけ選べばよい、という関係になっています。

実装例:Computedとリストを扱うTodoストア

カウンターより実務に近い例として、Todoリストを見ます。追加・完了トグルをActionに、未完了件数をComputedに寄せるのが典型的な設計です。

import { makeAutoObservable } from "mobx"

class TodoStore {
  todos = []

  constructor() {
    makeAutoObservable(this)
  }

  addTodo(title) {
    this.todos.push({ title, done: false })
  }

  toggle(index) {
    this.todos[index].done = !this.todos[index].done
  }

  get remaining() {
    return this.todos.filter((t) => !t.done).length
  }
}

export const todoStore = new TodoStore()

remainingはComputedなので、todosの中身が変わったときだけ再計算されます。この値をobserver付きコンポーネントで表示すれば、未完了件数のバッジは常に最新に保たれ、件数を手で数え直すコードは不要です。状態(todos)・変更(addTodotoggle)・派生(remaining)が3つの役割に分かれている点が、MobXらしい状態設計です。

MobX・Redux・Zustandの比較:どれを選ぶか

Reactの状態管理は選択肢が多く、MobXが常に最適とは限りません。代表的な3つを比べます。

観点 MobX Redux(RTK) Zustand
パラダイム 可変・リアクティブ 不変・単方向 可変・フック
記述量 少ない 多め(RTKで軽減) 最小
変更追跡 Action推奨 アクション必須で明確 set関数
向く規模 中〜大(要設計規律) 大・複数人 小〜中

使い分けの指針ははっきりしています。状態の変更履歴を厳密に追える単方向フローや、大人数での一貫性が最優先ならReduxが向きます。逆に、ストアを1ファイルで最小構成に収めたいならZustandが軽快です。MobXは「オブジェクトを直接書き換える手軽さ」と「自動追跡による最適な再描画」を両立したい中規模開発に合います。

一方で、MobXを避けたほうがよい場面もあります。状態変更の監査可能性を最重視するチーム、あるいは「どこで状態が変わったか」を厳密なルールで縛りたいプロジェクトでは、どこでも書き換えられるMobXの自由度がかえってリスクになります。その場合はActionへの集約とstrict modeを徹底するか、Reduxを選ぶべきです。各ライブラリの詳細はReduxの基本的な使い方Zustandの使い方も併せて確認してください。

よくある質問(FAQ)

MobXとは何ですか?株のMOBXとは違いますか?

本記事のMobXは、JavaScript/TypeScript向けの状態管理ライブラリ(mobx.js.org)です。検索結果には米国上場企業Mobix Labs(ティッカー「MOBX」)の株価情報も混在しますが、両者は無関係です。プログラミングで使うのはこのライブラリのほうです。

mobx-reactとmobx-react-liteの違いは?

mobx-react-liteは関数コンポーネント専用の軽量版(約1.5kB前後・gzip)で、Provider/injectを持ちません。mobx-reactはそれに加えてクラスコンポーネント対応とProvider/injectを備えます。関数コンポーネント中心の新規開発ならmobx-react-liteで十分です。

MobXのインストール方法は?

npmならnpm install mobx mobx-react-lite、yarnならyarn add mobx mobx-react-liteです。本体のmobxとReactバインディングを分けて入れます。クラス対応が必要ならmobx-react-litemobx-reactに置き換えます。

MobXとReduxはどちらを使うべきですか?

変更履歴の厳密な追跡や大人数での一貫性を優先するならRedux(Redux Toolkit)、記述量を抑えて手軽に書きたい中規模開発ならMobXが向きます。プロジェクトの規模とチームの運用ルールで選び分けるのが実務的です。

MobXは今も使われていますか?メンテナンスされていますか?

はい。MobXはメジャーバージョン6として継続的に更新されており(本記事時点で6.16系)、React以外のフレームワークでも利用されています。デコレータ非依存の書き方に統一された現行版は、ビルド設定を選ばず導入しやすくなっています。

関連記事

資料請求

RELATED POSTS 関連記事