redux、mobx、vuex は、アプリケーション全体のデータを管理するためのライブラリです。redux は予測可能な状態変化を重視し、アクションとレデューサーを通じてデータを変更します。mobx は観測可能な状態を使い、変更を自動的に検知して更新します。vuex は Vue.js 専用の公式状態管理で、中央集権的なストアを提供します。これらはそれぞれ異なる考え方に基づいており、プロジェクトの規模やチームの嗜好に合わせて選ぶ必要があります。
redux、mobx、vuex はいずれもアプリケーションの状態を管理するためのツールですが、その仕組みと使い勝手は大きく異なります。実務で直面する課題に対して、それぞれがどうアプローチするかを比較します。
redux は状態を直接変更できません。アクションを dispatch し、レデューサーが新しい状態を返します。
// redux: Action dispatch
store.dispatch({ type: 'INCREMENT' });
// reducer
function counter(state = 0, action) {
if (action.type === 'INCREMENT') return state + 1;
return state;
}
mobx は状態を直接変更できます。観測可能なプロパティを書き換えると、自動で反応します。
// mobx: Direct mutation
import { makeObservable, action, observable } from "mobx";
class Store {
count = 0;
constructor() {
makeObservable(this, { count: observable, increment: action });
}
increment() {
this.count++;
}
}
const store = new Store();
store.increment();
vuex はミューテーションを通じて状態を変更します。
// vuex: Commit mutation
store.commit('INCREMENT');
const store = createStore({
state: () => ({ count: 0 }),
mutations: {
INCREMENT(state) {
state.count++;
}
}
});
redux は構造を守るために多くのコードが必要です。
Redux Toolkit を使うことで大幅に削減できます。// redux (Traditional)
const ADD_TODO = 'ADD_TODO';
function addTodo(text) { return { type: ADD_TODO, text }; }
function todos(state = [], action) { /* ... */ }
mobx はクラスとデコレータ(または makeObservable)だけで済みます。
// mobx
class TodoStore {
todos = [];
addTodo(text) {
this.todos.push({ text });
}
}
vuex はストアの定義に一定の構造を要求します。
// vuex
export default createStore({
state: { todos: [] },
mutations: { add(state, text) { /*...*/ } },
actions: { addTodo({ commit }, text) { commit('add', text); } }
});
redux はフレームワークを選びません。
// redux + React
import { useSelector } from 'react-redux';
function Counter() {
const count = useSelector(state => state.count);
return <div>{count}</div>;
}
mobx もフレームワークを選ばない汎用ライブラリです。
// mobx + React
import { observer } from 'mobx-react-lite';
const Counter = observer(() => {
return <div>{store.count}</div>;
});
vuex は Vue.js 専用です。
// vuex + Vue
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();
return { count: computed(() => store.state.count) };
}
};
redux は現在も活発に開発されています。
redux パッケージよりも Redux Toolkit の使用が公式に推奨されています。// Redux Toolkit (Modern Standard)
import { createSlice } from '@reduxjs/toolkit';
const slice = createSlice({ name: 'counter', initialState: 0, reducers: { increment: state => state + 1 } });
mobx も安定して維持されています。
// mobx v6+
import { makeAutoObservable } from "mobx";
class Store {
constructor() { makeAutoObservable(this); }
}
vuex は Vue 3 において維持モードに入っています。
pinia の使用が公式に推奨されています。// Pinia (Recommended for Vue 3)
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }) });
これら 3 つのライブラリは、異なるアプローチを取りながらも、共通の目的を持っています。
// 全て共通:グローバルストアへのアクセス
const state = store.state; // vuex
const state = store.getState(); // redux
const state = store.count; // mobx
// 全て共通:DevTools 連携
// redux: Redux DevTools Extension
// mobx: MobX Developer Tools
// vuex: Vue Devtools
// 全て共通:テスト可能なロジック
// redux: reducer testing
// mobx: action testing
// vuex: mutation testing
| 特徴 | redux | mobx | vuex |
|---|---|---|---|
| 状態変更 | 间接(アクション) | 直接(代入) | 间接(ミューテーション) |
| 学習コスト | 高い(概念が多い) | 低い(直感的) | 中程度(Vue 依存) |
| コード量 | 多い(Toolkit で削減可) | 少ない | 中程度 |
| フレームワーク | 汎用(React 中心) | 汎用 | Vue 専用 |
| 現状 | 活発(Toolkit 推奨) | 安定 | 維持モード(Pinia 推奨) |
redux は、大規模で複雑な状態管理が必要な場合に最適です。特に React エコシステムでは事実上の標準であり、チーム規模が大きくなっても構造を維持できます。ただし、設定の手間を減らすために Redux Toolkit の使用を強く推奨します。
mobx は、開発速度とコードの簡潔さを優先する場合に適しています。状態変更の追跡が緩やかでも問題ないプロジェクトや、クラスベースの設計を好むチームに向いています。フレームワークを選ばない柔軟性も魅力です。
vuex は、Vue 2 の既存プロジェクトを維持する場合のみ選択してください。Vue 3 で新規開発を行う場合は、より簡潔で型サポートも強い pinia への移行を検討すべきです。状態管理ライブラリとしての役割は終わりに近づいています。
結論: 新規プロジェクトでは、React なら redux (Toolkit)、Vue なら pinia、フレームワークに縛られない柔軟性が必要なら mobx を選ぶのが賢明です。
少コードで状態管理を実装したい場合に適しています。クラスベースの設計やオブジェクト指向的な書き方を好むチームに向いています。魔法のような自動更新機能を使っても問題ない場合、開発速度を優先できます。ただし、状態変更の追跡が緩くなるため、大規模化すると挙動を把握しづらくなる可能性があります。
大規模なプロジェクトで、状態変化の履歴を追跡したい場合に最適です。データフローが明確になるため、チームでの共同開発やバグ調査がしやすくなります。標準的な書き方が確立されており、採用事例も豊富です。ただし、定型文が多いため、小規模なアプリではオーバースペックになることがあります。
Vue 2 を使用している既存プロジェクトでは依然として有効です。しかし、Vue 3 で新規開発する場合は、公式に推奨されている pinia の検討を強くお勧めします。vuex は維持モードに入っており、新しい機能追加は期待できません。Vue エコシステムに深く統合された状態管理が必要な場合のみ選定します。
Simple, scalable state management.
Documentation can be found at mobx.js.org.
MobX is made possible by the generosity of the sponsors below, and many other individual backers. Sponsoring directly impacts the longevity of this project.
🥇🥇 Platinum sponsors ($5000+ total contribution): 🥇🥇
🥇 Gold sponsors ($2500+ total contribution):
🥈 Silver sponsors ($500+ total contributions):
Anything that can be derived from the application state, should be. Automatically.
MobX is a signal based, battle-tested library that makes state management simple and scalable by transparently applying functional reactive programming. The philosophy behind MobX is simple:
Write minimalistic, boilerplate-free code that captures your intent. Trying to update a record field? Simply use a normal JavaScript assignment — the reactivity system will detect all your changes and propagate them out to where they are being used. No special tools are required when updating data in an asynchronous process.
All changes to and uses of your data are tracked at runtime, building a dependency tree that captures all relations between state and output. This guarantees that computations that depend on your state, like React components, run only when strictly needed. There is no need to manually optimize components with error-prone and sub-optimal techniques like memoization and selectors.
MobX is unopinionated and allows you to manage your application state outside of any UI framework. This makes your code decoupled, portable, and above all, easily testable.
So what does code that uses MobX look like?
import React from "react"
import ReactDOM from "react-dom"
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react-lite"
// Model the application state.
const myTimer = makeAutoObservable({
secondsPassed: 0,
increase() {
this.secondsPassed += 1
},
reset() {
this.secondsPassed = 0
}
})
// Build a "user interface" that uses the observable state.
const TimerView = observer(({ timer }) => (
<button onClick={() => timer.reset()}>Seconds passed: {timer.secondsPassed}</button>
))
ReactDOM.render(<TimerView timer={myTimer} />, document.body)
// Update the 'Seconds passed: X' text every second.
setInterval(() => {
myTimer.increase()
}, 1000)
The observer wrapper around the TimerView React component will automatically detect that rendering
depends on the timer.secondsPassed observable, even though this relationship is not explicitly defined. The reactivity system will take care of re-rendering the component when precisely that field is updated in the future.
Every event (onClick / setInterval) invokes an action (myTimer.increase / myTimer.reset) that updates observable state (myTimer.secondsPassed).
Changes in the observable state are propagated precisely to all computations and side effects (TimerView) that depend on the changes being made.
This conceptual picture can be applied to the above example, or any other application using MobX.
To learn about the core concepts of MobX using a larger example, check out The gist of MobX page, or take the 10 minute interactive introduction to MobX and React. The philosophy and benefits of the mental model provided by MobX are also described in great detail in the blog posts UI as an afterthought and How to decouple state and UI (a.k.a. you don’t need componentWillMount).
The MobX Quick Start Guide ($24.99) by Pavan Podila and Michel Weststrate is available as an ebook, paperback, and on the O'Reilly platform (see preview).
MobX is inspired by reactive programming principles, which are for example used in spreadsheets. It is inspired by model–view–viewmodel frameworks like MeteorJS's Tracker, Knockout and Vue.js, but MobX brings transparent functional reactive programming (TFRP, a concept which is further explained in the MobX book) to the next level and provides a standalone implementation. It implements TFRP in a glitch-free, synchronous, predictable and efficient manner.
A ton of credit goes to Mendix, for providing the flexibility and support to maintain MobX and the chance to prove the philosophy of MobX in a real, complex, performance critical applications.