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-liteのobserverで連携する。クラスコンポーネントや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ではデコレータが既定ではなくなり、コンストラクタでmakeObservable/makeAutoObservableを呼ぶ方式が標準になりました。ビルド設定なしでどの環境でも動く形に統一するための変更です。
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引数でobservable/action/computedを指定します。継承したクラスや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-liteとmobx-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)・変更(addTodo/toggle)・派生(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-liteをmobx-reactに置き換えます。
MobXとReduxはどちらを使うべきですか?
変更履歴の厳密な追跡や大人数での一貫性を優先するならRedux(Redux Toolkit)、記述量を抑えて手軽に書きたい中規模開発ならMobXが向きます。プロジェクトの規模とチームの運用ルールで選び分けるのが実務的です。
MobXは今も使われていますか?メンテナンスされていますか?
はい。MobXはメジャーバージョン6として継続的に更新されており(本記事時点で6.16系)、React以外のフレームワークでも利用されています。デコレータ非依存の書き方に統一された現行版は、ビルド設定を選ばず導入しやすくなっています。