effector、mobx、redux、rxjs は、JavaScript アプリケーションの状態管理と非同期処理を扱うための代表的なライブラリです。redux は予測可能な状態コンテナとして広く採用されており、mobx は可変状態と自動依存関係追跡を特徴とします。effector は型安全性とパフォーマンスを重視した関数型アプローチを提供し、rxjs はストリームベースのリアクティブプログラミングに特化しています。これらはそれぞれ異なる哲学を持ち、プロジェクトの規模や複雑さ、チームの好みに合わせて選択されます。
effector、mobx、redux、rxjs は、JavaScript アプリケーションの状態管理と非同期処理を扱うための代表的なライブラリですが、それぞれアプローチが異なります。redux は不変性と単一方向データフローを重視し、mobx は可変状態と自動反応を特徴とします。effector はイベント駆動の型安全な状態管理を提供し、rxjs はストリームベースの処理に特化しています。実務でこれらをどう使い分けるか、具体的なコードを通じて比較します。
状態をどのように宣言するかは、ライブラリの哲学を如実に表しています。
redux は状態を単一のオブジェクト(ストア)で管理し、変更はレデューサーを通じて行います。
@reduxjs/toolkit の createSlice を使うのが現在の標準です。// redux: スライスによる状態定義
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; }
}
});
export const { increment } = counterSlice.actions;
mobx はクラスやオブジェクトをそのまま状態として扱います。
makeAutoObservable で自動的に反応性を付与します。// mobx: クラスによる状態定義
import { makeAutoObservable } from 'mobx';
class Counter {
value = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.value += 1;
}
}
effector はストアとイベントを分離して定義します。
createStore、更新は createEvent でトリガーします。// effector: ストアとイベントの定義
import { createStore, createEvent } from 'effector';
const increment = createEvent();
const $counter = createStore(0).on(increment, (count) => count + 1);
rxjs は状態を「時間の経過とともに変化する値のストリーム」として扱います。
BehaviorSubject を使うと現在の値を保持できます。// rxjs: BehaviorSubject による状態定義
import { BehaviorSubject } from 'rxjs';
const counter$ = new BehaviorSubject(0);
// 値の更新は next() で行います
counter$.next(counter$.value + 1);
状態を変更する際の書き方も大きく異なります。
redux はアクションをディスパッチすることで間接的に状態を変更します。
// redux: アクションのディスパッチ
import { useDispatch } from 'react-redux';
function Counter() {
const dispatch = useDispatch();
return <button onClick={() => dispatch(increment())}>+</button>;
}
mobx はメソッド内で直接状態を変更します。
// mobx: 直接の変更
import { observer } from 'mobx-react-lite';
function Counter({ store }) {
return <button onClick={() => store.increment()}>+</button>;
}
effector はイベントをトリガー(呼ぶ)ことで状態を更新します。
// effector: イベントのトリガー
import { useUnit } from 'effector-react';
function Counter() {
const increment = useUnit(incrementEvent);
return <button onClick={increment}>+</button>;
}
rxjs はストリームに新しい値を送り込みます。
Subject や BehaviorSubject の next メソッドを使います。// rxjs: ストリームへの値送信
const button = document.querySelector('button');
button.addEventListener('click', () => {
counter$.next(counter$.value + 1);
});
API 通信などの非同期処理をどう扱うかも重要な選定基準です。
redux はミドルウェア(thunk)や createAsyncThunk を使います。
// redux: 非同期スライス
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
const fetchData = createAsyncThunk('data/fetch', async () => {
const res = await fetch('/api');
return res.json();
});
const slice = createSlice({
name: 'data',
initialState: { status: 'idle', data: null },
extraReducers: (builder) => {
builder.addCase(fetchData.fulfilled, (state, action) => {
state.data = action.payload;
});
}
});
mobx は async/await をアクション内でそのまま使えます。
// mobx: 非同期アクション
class Store {
data = null;
async fetchData() {
const res = await fetch('/api');
this.data = await res.json();
}
}
effector は createEffect を使って副作用を管理します。
done)と失敗時(fail)のイベントが自動生成されます。// effector: 効果(Effect)
import { createEffect } from 'effector';
const fetchData = createEffect(async () => {
const res = await fetch('/api');
return res.json();
});
// 結果をストアに繋ぐ
const $data = createStore(null).on(fetchData.doneData, (_, payload) => payload);
rxjs はオペレーター(switchMap, mergeMap など)で制御します。
// rxjs: オペレーターによる制御
import { fromEvent } from 'rxjs';
import { switchMap } from 'rxjs/operators';
fromEvent(button, 'click').pipe(
switchMap(() => fetch('/api'))
).subscribe(res => {
// 処理結果
});
React コンポーネント内でどう状態を使うかも開発体験に影響します。
redux は useSelector と useDispatch が標準です。
// redux: フックによる連携
import { useSelector } from 'react-redux';
function Counter() {
const count = useSelector((state) => state.counter.value);
return <div>{count}</div>;
}
mobx は observer 高階コンポーネントまたはフックを使います。
// mobx: observer による連携
import { observer } from 'mobx-react-lite';
const Counter = observer(({ store }) => {
return <div>{store.value}</div>;
});
effector は useUnit フックを使用します。
// effector: useUnit による連携
import { useUnit } from 'effector-react';
function Counter() {
const { count, inc } = useUnit({ count: $counter, inc: increment });
return <div onClick={inc}>{count}</div>;
}
rxjs は専用のフック(useObservable 等)を自作またはライブラリから入れます。
// rxjs: カスタムフックによる連携(例)
import { useObservable } from 'react-rxjs'; // 第三者ライブラリ例
function Counter() {
const [count] = useObservable(() => counter$, 0);
return <div>{count}</div>;
}
これら 4 つのライブラリは、異なるアプローチを取りながらも、いくつかの重要な共通点を持っています。
// 全てのライブラリに共通する概念:変化の通知
// redux: dispatch -> reducer -> store update
// mobx: action -> observable update -> reaction
// effector: event -> store update -> subscription
// rxjs: next() -> observable stream -> subscribe
// 例:Redux DevTools, MobX DevTools, Effector Inspector
// 各ライブラリはブラウザ拡張やロギング機能で状態変化を可視化します
// 例:redux-persist, mobx-persist-store, effector-storage
// 状態を localStorage などに保存する仕組みが各エコシステムに存在します
| 特徴 | redux | mobx | effector | rxjs |
|---|---|---|---|---|
| 状態管理 | 不変(Immutable) | 可変(Mutable) | 不変(Immutable) | ストリーム |
| 更新方法 | アクションディスパッチ | 直接変更 | イベントトリガー | next() |
| 非同期 | Thunk / RTK Query | async/await | Effects | Operators |
| React 連携 | useSelector | observer | useUnit | Custom Hook |
| 学習コスト | 中(規約が多い) | 低(直感的) | 中(概念理解) | 高(オペレーター) |
| 型安全性 | 高(RTK + TS) | 中 | 非常に高い | 高 |
redux は、標準的で予測可能な状態管理を求める場合に最適です。特にチーム開発で規約を統一したい時や、デバッグ機能を重視する時に選ばれます。現在は Redux Toolkit を使うことで、以前より手軽に導入できます。
mobx は、少人数のチームや、オブジェクト指向的な設計を好むプロジェクトに向いています。ボイラープレートを減らして素早く開発したい場合に有効ですが、大規模化すると状態の変更箇所が追跡しにくくなるリスクがあります。
effector は、型安全性とパフォーマンスを両立したいモダンなプロジェクトに推奨されます。イベント駆動アーキテクチャを採用することで、複雑なロジックも整理しやすく、テストも容易です。
rxjs は、状態管理そのものというより、複雑な非同期フローやイベント処理を制御するためのツールとして捉えるべきです。フォームのバリデーション、リアルタイム通信、キャンセル処理など、ストリーム処理が本領を発揮する場面で選択します。
最終的なアドバイス:単純な状態管理なら redux か effector、複雑な非同期フロー制御なら rxjs、手軽さと可変状態の利便性なら mobx を検討してください。プロジェクトの要件とチームの習熟度に合わせて選定することが重要です。
型安全性とパフォーマンスを最優先する場合に effector を選択します。ボイラープレートが少なく、イベント駆動のアーキテクチャで複雑なロジックを整理したいプロジェクトに適しています。特に TypeScript との相性が良く、大規模な状態管理が必要な場合に有効です。
オブジェクト指向的な可変状態管理を好む場合に mobx を選択します。クラスやオブジェクトを直接変更できるため、学習コストが低く、既存の OOP 知識を活かせます。複雑な依存関係を持つ状態グラフを扱う際に強力です。
予測可能な状態更新と豊富なエコシステムを必要とする場合に redux(特に Redux Toolkit)を選択します。デバッグツール(Time Travel)やミドルウェアによる拡張性が求められる大規模プロジェクトや、多くのサードパーティライブラリとの連携が必要な場合に適しています。
複雑な非同期ストリームやイベント処理を扱う場合に rxjs を選択します。状態管理そのものよりも、データフローの変換や制御、キャンセル処理などを細かく制御したい時に真価を発揮します。リアルタイムデータ処理やフォーム制御などに適しています。
Business logic with ease
Effector implements business logic with ease for Javascript apps (React/React Native/Vue/Svelte/Node.js/Vanilla), allows you to manage data flow in complex applications. Effector provides best TypeScript support out of the box.
You can use any package manager
npm add effector
React
To getting started read our article how to write React and Typescript application.
npm add effector effector-react
SolidJS
npm add effector effector-solid
Vue
npm add effector effector-vue
Svelte
Svelte works with effector out of the box, no additional packages needed. See word chain game application written with svelte and effector.
CDN
For additional information, guides and api reference visit our documentation site
effector to your project's home pageYou can try effector with online playground
Code sharing, Typescript and react supported out of the box. Playground repository
Use effector-logger for printing updates to console, displaying current store values with ui or connecting application to familiar redux devtools
Your support allows us to improve the developer experience 🧡.