Reduxの(簡単な)歴史
2011年:JS MVCフレームワーク
AngularJS、Ember、Backboneといった初期のJavaScript MVCフレームワークには問題がありました。AngularJSは「コントローラー」とテンプレートの分離を強制しようとしましたが、テンプレートに<div onClick="$ctrl.some.deeply.nested.field = 123">
と書いてしまうことを防ぐことはできませんでした。一方、Backboneはイベントエミッターをベースにしており、モデル、コレクション、ビューはそれぞれイベントを発行できました。モデルは"change:firstName"
イベントを発行し、ビューはそれに購読します。しかし、*あらゆる*コードがこれらのイベントに購読してより多くのロジックを実行することができ、それが*さらに多くの*イベントをトリガーする可能性がありました。
そのため、これらのフレームワークのデバッグと保守は非常に困難でした。1つのモデルの1つのフィールドを更新すると、アプリ全体で数十のイベントとロジックが実行されたり、どのテンプレートでもいつでも状態を変更できる可能性があり、状態を更新したときに何が起こるかを理解することが不可能でした。
2014年:Flux
2012年から2013年頃、Reactが初めて公開された頃、Facebookは社内で数年間にわたってReactを使用していました。「未読の通知がいくつあるか」など、同じデータにアクセスする必要があるUIの独立した部分が複数ありましたが、Backboneスタイルのコードを使用すると、そのロジックを整理するのが難しいことがわかりました。
Facebookは最終的に「Flux」と呼ばれるパターンを考案しました。PostsStore
やCommentsStore
のように、複数のシングルトンストアを作成します。これらのストアインスタンスはそれぞれDispatcher
に登録され、ストアの更新をトリガーする*唯一の*方法はDispatcher.dispatch({type: "somethingHappened"})
を呼び出すことでした。このプレーンオブジェクトは「アクション」と呼ばれていました。すべての状態更新ロジックを半ば一元化するという考え方でした。アプリのランダムな部分で状態を変更することはできず、すべての状態更新は予測可能になります。
Facebookはこの「Fluxアーキテクチャ」の概念を2014年頃に発表しましたが、そのパターンを実装した完全なライブラリは提供しませんでした。そのため、Reactコミュニティは、このパターンを参考に、さまざまなバリエーションを持つ*数十*のFlux inspiredライブラリを構築しました。
2015年:Reduxの誕生
2015年中頃、Dan AbramovはReduxと呼ばれる、もう1つのFlux inspiredライブラリの構築を始めました。そのアイデアは、カンファレンストークのために「タイムトラベルデバッグ」を実演することでした。このライブラリはFluxパターンを使用するように設計されましたが、いくつかの関数型プログラミングの原則が適用されていました。ストアの*インスタンス*ではなく、イミュータブルな更新を行う予測可能なreducer関数を使用できました。これにより、時間を前後に行き来して、さまざまな時点での状態がどのように見えるかを確認できるようになります。また、コードがよりシンプルで、テスト可能で、理解しやすくなります。
Reduxは2015年に登場し、他のすべてのFlux inspiredライブラリをあっという間に駆逐しました。Reactエコシステムの先進的な開発者から早期に採用され、2016年までに多くの人が「Reactを使用しているなら、Reduxも*必ず*使用しなければならない」と言うようになりました。(率直に言って、これは多くの人がReduxを*必要としない*場所で使用するようになりました!)
当時、Reactには*レガシー* Context APIしかなく、それは基本的に壊れていました。*更新された*値を適切に下に渡すことができませんでした。そのため、Contextにイベントエミッターを入れて購読することはできましたが、プレーンデータには実際には使用できませんでした。そのため、多くの人がReduxを採用し始めました。なぜなら、Reduxは更新された値をアプリケーション全体に一貫して渡す方法*だった*からです。
Danは初期に「Reduxはコードを書くための*最短の*方法であることを意図したものではなく、予測可能で理解しやすいものにすることを意図したものである」と述べています。その一部は一貫したパターンを持つことです(状態の更新はreducerによって行われるため、常にreducerロジックを見て、状態の値が*どうなるか*、可能なアクションは何か、そしてそれらがどのような更新を引き起こすかを確認します)。また、ロジックをコンポーネントツリーから*外に*移動することで、UIはほとんど「このことが起こった」と言うだけで、コンポーネントがシンプルになります。それに伴い、reducerやセレクターのような「純粋関数」として書かれたコードは、理解するのがより簡単になります。引数を入力し、結果が出力され、他に何も見る必要はありません。最後に、Reduxの設計により、Redux DevToolsが可能になりました。Redux DevToolsは、ディスパッチされたすべてのアクション、アクション/状態に含まれていた内容、および各アクションで発生した変更の読みやすいリストを表示します。
初期のReduxパターンは、特にボイラープレートが多かったです。単一のアクションタイプ(const ADD_TODO = "ADD_TODO"
)、アクションクリエーター関数、およびreducerケースを定義するためだけに、actions/todos.js
、reducers/todos.js
、およびconstants/todos.js
を持つのが一般的でした。また、スプレッド演算子を使用してイミュータブルな更新を手書きする必要があり、これは間違えやすかったです。Reduxでサーバーの状態を取得してキャッシュしましたが、フェッチを行うサンクを作成し、フェッチされたデータでアクションをディスパッチし、reducerでキャッシュの状態を管理するために、手動で多くのコードを記述する必要がありました。
Reduxはそのボイラープレート*にもかかわらず*人気を博しましたが、それは常に最大の懸念事項でした。
2017年:エコシステム競争
2017年から2018年までに、状況は変わりました。コミュニティの多くは、「クライアント側の状態管理」ではなく「データのフェッチとキャッシュ」に焦点を当てるようになり、データフェッチのためのApollo、React Query、SWR、Urqlなどのライブラリが登場しました。同時に、*新しい* React Context APIも登場し、これは更新された値をコンポーネントツリーに適切に渡します。
これは、Reduxが以前ほど「必須」ではなくなったことを意味します。同じ問題の多くを解決する他のツールが、さまざまな程度の重複(そして多くの場合、より少ないコード)で登場しました。「ボイラープレート」に関する頻繁な苦情は、Reduxを使用している人々から多くの懸念を引き起こしました。
2019年:Redux Toolkit
そこで、2019年に、より少ないコードで同じReduxロジックを記述するためのより簡単な方法として、Redux Toolkitを構築して出荷しました。RTKは依然として「Redux」です(単一のストア、アクションをディスパッチしてイミュータブルな更新ロジックを介してreducerで状態更新をトリガーします)が、APIがよりシンプルで、組み込みのデフォルト動作が向上しています。これには、React QueryとApolloにインスパイアされた、組み込みのデータフェッチおよびキャッシュライブラリであるRTK Queryも含まれています。
今日、RTKはReduxロジックを記述するための標準的な方法です。すべてのツールと同様に、トレードオフがあります。RTKはZustandを使用するよりも少し多くのコードになる可能性がありますが、アプリロジックをUIから分離するための便利なパターンも提供します.Reduxはすべてのアプリに適したツールではありませんが、Reactアプリで最も広く使用されている状態管理ライブラリであり、優れたドキュメントがあり、一貫性のある予測可能な構造でアプリを構築するのに役立つ多くの機能を提供しています。