
TL; DR
ContextとReduxは同じですか?
番号。それらは異なることを行う異なるツールであり、異なる目的で使用されます。
コンテキストは「状態管理」のツールですか?
番号。コンテキストは、依存性注入の一形態です。何も制御しないトランスポートメカニズムです。「状態管理」は手動で行われ、通常はuseState()/ useReducer()フックを使用します。
ContextとuseReducer()はReduxの代わりになりますか?
番号。それらはやや類似しており、部分的に重複していますが、機能の点で大きく異なります。
いつコンテキストを使用する必要がありますか?
一部のデータを複数のコンポーネントで使用できるようにしたいが、そのデータをコンポーネントツリーのすべてのレベルで小道具として渡したくない場合。
いつContextとuseReducer()を使用する必要がありますか?
アプリケーションの特定の部分で適度に複雑なコンポーネントの状態を管理する必要がある場合。
いつReduxを使うべきですか?
Reduxは次の場合に最も役立ちます。
- 同じデータを使用する多数のステートフルコンポーネント
- アプリのステータスは頻繁に更新されます
- 状態を更新するための複雑なロジック
- アプリケーションには中規模から大規模のコードベースがあり、多くの人がそれに取り組んでいます
- アプリケーションの状態がいつ、なぜ、どのように更新されるかを知り、それらの変更を視覚化できるようにしたい
- 副作用、安定性(永続性)、およびデータのシリアル化を処理するには、より強力な機能が必要です
コンテキストとReduxを理解する
ツールを正しく使用するには、次のことを理解することが重要です。
- それはなんのためですか
- それはどのようなタスクを解決しますか
- いつ、なぜ作成されたのか
また、アプリケーションで解決しようとしている問題を理解し、それらを最もよく解決するツールを使用することも重要です。誰かがそれらを使用するように指示したからではなく、人気があるからではなく、特定の状況。
ContextとReduxに関する混乱は、主に、これらのツールの目的と解決するタスクについての誤解によるものです。したがって、いつ使用すべきかを話す前に、それらが何であるか、そしてそれらがどのような問題を解決するかを決定する必要があります。
コンテキストとは何ですか?
公式ドキュメントからコンテキストを定義することから始めましょう 。
「コンテキストにより、中間レベルで小道具を渡すことなく、データをコンポーネントツリーに渡すことができます。
典型的なReactアプリケーションでは、データは小道具を使用して上から下に(親から子に)渡されます。ただし、この方法は、アプリケーションの多くのコンポーネントに渡す必要がある一部のタイプの小道具(たとえば、選択した言語、インターフェイステーマ)ではやり過ぎになる可能性があります。コンテキストは、コンポーネントツリーの各レベルで小道具を明示的に渡すことなく、そのようなデータをコンポーネント間で分散する方法を提供します。」
この定義は「管理」についての言葉ではなく、「転送」と「配布」についてのみ述べていることに注意してください。
現在のコンテキストAPI(React.createContext())は、以前のバージョンのReactで利用可能なレガシーAPIの代わりとして、React 16.3で最初に導入されましたが、いくつかの設計上の欠陥があります。主な問題の1つは、コンポーネントがshouldComponentUpdate()によるレンダリングをスキップした場合、コンテキストを介して渡された値の更新が「ブロック」される可能性があることでした。多くのコンポーネントが最適化の目的でshouldComponentUpdate()に頼っていたため、コンテキストを介してデータを渡すことは役に立たなくなりました。createContext()はこの問題に対処するように設計されているため、中間コンポーネントがレンダリングをスキップした場合でも、値の更新は子コンポーネントに反映されます。
コンテキストの使用
アプリケーションでコンテキストを使用することは、次のことを前提としています。
- const MyContext = React.createContext()を呼び出して、コンテキストオブジェクトをインスタンス化します
- 親コンポーネントで、render&ltMyContext.Provider value = {someValue}>。これにより、一部のデータがコンテキストに組み込まれます。このデータは、文字列、数値、オブジェクト、配列、クラスインスタンス、イベントハンドラーなど、何でもかまいません。
- const theContextValue = useContext(MyContext)を呼び出して、プロバイダー内の任意のコンポーネントのコンテキスト値を取得します
親コンポーネントが更新され、新しい参照がプロバイダー値として渡されると、コンテキストを「消費」するコンポーネントは強制的に更新されます。
通常、コンテキスト値はコンポーネントの状態です。
import { createContext } from 'react'
export const MyContext = createContext()
export function ParentComponent({ children }) {
const [counter, setCounter] = useState(0)
return (
<MyContext.Provider value={[counter, setCounter]}>
{children}
</MyContext.Provider>
)
}
次に、子コンポーネントはuseContext()フックを呼び出して、コンテキスト値を読み取ることができます。
import { useContext } from 'react'
import { MyContext } from './MyContext'
export function NestedChildComponent() {
const [counter, setCounter] = useContext(MyContext)
// ...
}
コンテキストが実際には何も制御していないことがわかります。代わりに、それは一種のパイプです。 <MyContext.Provider>を使用してトンネルの先頭(上部)にデータを配置すると、コンポーネントがuseContext(MyContext)を使用してデータを要求するまで、このデータはロールダウンされます。
したがって、コンテキストの主な目的は、支柱の穴あけを防ぐことです。コンポーネントツリーのすべてのレベルでデータを小道具として渡す代わりに、<MyContext.Provider>にネストされたコンポーネントは、useContext(MyContext)を介してデータにアクセスできます。これにより、小道具の受け渡しロジックを実装するためのコードを記述する必要がなくなります。
概念的には、これは依存性注入の一形態 です..。子供が特定のタイプのデータを必要としていることはわかっていますが、このデータを自分で作成または設定しようとはしていません。代わりに、実行時にこのデータを渡すために、いくつかの祖先に依存しています。
Reduxとは何ですか?
Redux Basicsの定義は次のとおり です。
「Reduxは、操作と呼ばれるイベントを使用してアプリケーションの状態を管理および更新するためのデザインパターンおよびライブラリです。Reduxは、予測可能な状態の更新を保証するルールに従って、アプリケーション状態の集中リポジトリとして機能します。
Reduxを使用すると、「グローバル」状態(アプリケーションの複数の部分にパイプされる状態)を管理できます。
Reduxが提供するパターンとツールにより、状態がどこで、いつ、なぜ、どのように更新されたか、およびアプリケーションがその変更にどのように応答したかを簡単に判断できます。」
この説明は次のことを示していることに注意してください。
- 状態管理
- Reduxの目的は、状態の変化が発生した理由と方法を特定することです。
Reduxは元々、Reactがリリースされてから1年後の2014年にFacebookによって開発されたデザインパターンである「Fluxarchitecture」の実装でした 。Fluxの登場以来、コミュニティはこの概念をさまざまな方法で実装する多くのライブラリを開発してきました。Reduxは2015年に登場し、その思慮深いデザイン、一般的な問題の解決、Reactとの優れた互換性のおかげで、すぐにこのコンテストの優勝者になりました。
アーキテクチャ上、Reduxは関数型プログラミングの原則を強調して使用しています。これにより、予測可能な「レデューサー」の形式でコードを記述し、「何が起こったのか」という概念を「これが発生したときに状態がどのように更新されるか」を定義するロジックから分離できます。イベントが発生します。」Reduxは、副作用の処理など、ストアの機能を拡張する方法としてミドルウェアも使用し ます。
Reduxは、操作の履歴と時間の経過に伴う状態の変化を調査するための開発者ツールも提供 します。
ReduxとReact
Redux自体はUIから独立しており、任意のビューレイヤー(React、Vue、Angular、vanilla JSなど)で使用することも、UIをまったく使用しないこともできます。
ただし、ほとんどの場合、ReduxはReactと組み合わせて使用されます。React Reduxライブラリ は、ReactコンポーネントがRedux状態から値を取得して操作を開始することにより、Reduxストアと対話できるようにする公式のUIバインディングレイヤーです。 React-Reduxは内部的にコンテキストを使用します。ただし、React-Reduxは、現在の状態値ではなく、Reduxストアインスタンスのコンテキストを通過することに注意してください 。これは、依存性注入にコンテキストを使用する例です。Reduxに接続されたコンポーネントがReduxストアと対話する必要があることはわかっていますが、コンポーネントを定義するときにそのストアが何であるかはわかりません。実際のReduxストアは、React-Reduxが提供する<Provider>コンポーネントを使用して、実行時にツリーに挿入されます。
したがって、React-Reduxを使用して、「ドリル」を防止することもできます(コンテキストの内部使用による)。<MyContext.Provider>を介して新しい値を明示的に渡す代わりに、このデータをReduxストアに入れて、目的のコンポーネントで取得できます。
(React-)Reduxの目的とユースケース
公式ドキュメントによると、Reduxの主な目的は次のとおりです。
「Reduxが提供するパターンとツールにより、状態の変化がいつ、どこで、なぜ、どのように発生し、アプリケーションがそれにどのように反応したかを簡単に理解できます。」
Reduxを使用する理由は他にもいくつかあります。これらの理由の1つは、「穴あけ」を防ぐためです。
その他の使用例:
- 状態管理ロジックとUIレイヤーの完全な分離
- 異なるUIレイヤー間での状態管理ロジックの分散(たとえば、アプリケーションをAngularJSからReactに変換するプロセス)
- Reduxミドルウェアを使用して操作を初期化するときにロジックを追加する
- Redux状態の一部を保存する機能
- 他の開発者が再現できるバグレポートを受信する機能
- 開発中にロジックとUIをすばやくデバッグする機能
Dan Abramovは、2016年の記事「なぜReduxが必要ないのか」にこれらのケースをリストしました 。
コンテキストが「状態管理」のツールではないのはなぜですか?
状態は、アプリケーションの動作を説明するデータです。必要に応じて、状態をサーバー状態、通信状態、ローカル状態などのカテゴリに分類できますが、重要な側面は、データの保存、読み取り、更新、および使用です。
XStateライブラリの作成者で状態管理のスペシャリストであるDavidKhourshidは、ツイートの1つで、
「状態管理とは、時間の経過とともに状態を変化させることです」と述べています。
したがって、「状態管理」とは次のことを意味します。
- 初期値の保存
- 現在の値を取得する
- 値の更新
また、通常、現在の状態値が変更されたときに通知を受け取る方法があります。
ReactフックのuseState()とuseReducer()は、状態管理の優れた例です。これらのフックを使用すると、次のことができます。
- フックを呼び出して初期値を保存する
- フックを呼び出して現在の値を取得する
- setState()またはdispatch()をそれぞれ呼び出して、値を更新します
- コンポーネントを再レンダリングして、状態の更新に注意してください
ReduxとMobXでは、状態を管理することもできます。
- Reduxはルートレデューサーを呼び出すことで初期値を保存し、store.getState()で現在の値を読み取り、store.dispatch(アクション)で値を更新し、store.subscribe(リスナー)を介して状態更新通知を受信できるようにします
- MobXは、ストレージクラスフィールドに値を割り当てることで初期値を保持し、ストレージフィールドを介して現在の値を読み取って更新できるようにし、autorun()メソッドとcomputed()メソッドを使用して状態更新通知を受信します。
状態管理ツールには、React-Query、SWR、Apollo、Urqlなどのサーバーキャッシュツールも含まれます-フェッチされたデータに基づいて初期値を保存し、フックを使用して現在の値を返し、「サーバーミューテーション」を介して値を更新し、通知することができますコンポーネントを再レンダリングすることによる変更。
ReactContextが指定された基準を満たしていません。したがって、それは状態管理ツールではありません
前述のように、コンテキスト自体は何も格納しません。<MyContext.Provider>をレンダリングする親コンポーネントは、値を渡す役割を果たします。値は通常、コンポーネントの状態によって異なります。実際の「状態管理」は、useState()/ useReducer()フックから取得されます。
David Khourshid
は、次のようにも述べています。「コンテキストとは、既存の状態がコンポーネント間で共有される方法です。コンテキストは状態とは何の関係もありません。」
そして 後のツイートで、
「コンテキストは、状態を抽象化する隠された小道具のようなものだと思います」。
コンテキストが行うことはすべて、「ドリル」を回避することです。
コンテキストとReduxの比較
コンテキストとReact + Reduxの機能を比較してみましょう。
- 環境
-
- 何も保存せず、何も管理しません
- Reactコンポーネントでのみ機能します
- 単純な(単一の)値の下を通過します。これは、任意(プリミティブ、オブジェクト、クラスなど)にすることができます。
- この簡単な意味を読みましょう
- 「穴あけ」を防ぐために使用できます
- 開発ツールのプロバイダーコンポーネントとコンシューマーコンポーネントの現在の値を表示しますが、この値への変更の履歴は表示しません
- 値が変更されたときに消費コンポーネントを更新しますが、更新をスキップすることはできません
- 副作用を処理するためのメカニズムを提供しません-レンダリングのみを担当します
- React + Redux
-
- 単純な値(通常はオブジェクト)を格納および管理します
- Reactコンポーネントの外部だけでなく、任意のUIで動作します
- この簡単な意味を読みましょう
- 「穴あけ」を防ぐために使用できます
- 操作を初期化し、レデューサーを実行することで値を更新できます
- 開発者ツールは、操作の初期化と状態変更の履歴を表示します
- ミドルウェアを使用して副作用を処理する機能を提供します
- コンポーネントがストアの更新をサブスクライブし、ストア状態の特定の部分を取得し、コンポーネントの再レンダリングを制御できるようにします
明らかに、これらは異なる機能を備えた完全に異なるツールです。それらの間の唯一の交点は、「穴あけ」を防ぐことです。
コンテキストとuseReducer()
「ContextvsRedux」の議論の問題の1つは、人々が実際に「useReducer()を使用して状態とコンテキストを管理し、値を渡す」という意味であることが多いことです。しかし、代わりに、彼らは「私はコンテキストを使用しています」と言うだけです。私の意見では、これが、文脈が「国家を支配している」という神話の維持に寄与する混乱の主な理由です。
Context + useReducer()の組み合わせを検討してください。はい、この組み合わせはRedux + React-Reduxと非常によく似ています。これらの組み合わせは両方とも次のとおりです。
- プリペイド
- レデューサー機能
- 操作を初期化する機能
- 値を渡し、ネストされたコンポーネントで読み取る機能
ただし、それらの間にはまだいくつかの重要な違いがあり、それらの機能と動作に表れています。私は、これらの違いを指摘してきた 、リアクトReduxのを、とコンテキスト行動と リアクトでのレンダリングに(ほぼ)完全ガイド。要約すると、次の点に注意してください。
- Context + useReducer()は、現在の値をコンテキストに渡すことに依存しています。React-Reduxは現在のReduxストアインスタンスをコンテキストに渡します
- つまり、useReducer()が新しい値を生成すると、一部のデータのみを使用している場合でも、コンテキストにサブスクライブされているすべてのコンポーネントが強制的に再描画されます。これにより、状態値のサイズ、署名されたコンポーネントの数、および再レンダリングの頻度によっては、パフォーマンスの問題が発生する可能性があります。React-Reduxを使用すると、コンポーネントはストア値の特定の部分をサブスクライブし、その部分が変更された場合にのみ再描画できます
他にも重要な違いがあります。
- Context + useReducer()はReactの組み込み機能であり、それ以外では使用できません。ReduxストアはUIに依存しないため、Reactとは別に使用できます
- React DevTools , . Redux DevTools , ( , type and payload),
- useReducer() middleware. useEffect() useReducer(), useReducer() middleware, Redux middleware
ここでは何である セバスチャンMarkbageは(コアチームのアーキテクトと反応)コンテキストを使用してについて語った:
「私の個人的な意見は、新たなコンテキストが(例えばローカライズやテーマなど)可能性は低い低周波の更新のために使用する準備ができているということです。また、古いコンテキストが使用されたすべての場合に使用できます。静的な値の場合、サブスクリプションによる更新のその後の配布。 Fluxのような州のディストリビューターの代わりとして使用する準備はできていません。」
状態のさまざまな部分に複数の個別のコンテキストを設定し、不要な再レンダリングを回避し、スコープの問題を解決することを推奨するWeb上の記事はたくさんあります。一部の投稿では、独自の「コンテキストコンポーネント」を追加することも提案され ています。これには、React.memo()、useMemo()の組み合わせが必要であり、アプリケーションの各部分でコードを2つのコンテキストに適切 に分割する必要があります(1つはデータ用、もう1つは更新機能)。もちろん、この方法でコードを書くこともできますが、この場合、React-Reduxを再発明しています。
したがって、Context + useReducer()は最初の概算でRedux + React-Reduxの軽量な代替手段ですが、これらの組み合わせは同一ではありませんが、context + useReducer()はReduxを完全に置き換えることはできません!
適切なツールの選択
適切なツールを選択するには、ツールが解決するタスクと、直面するタスクを理解することが非常に重要です。
ユースケースの概要
- 環境
- 「ドリル」なしでネストされたコンポーネントにデータを転送する
- useReducer()
- レデューサー機能を使用して複雑なコンポーネントの状態を制御する
- コンテキスト+ useReducer()
- レデューサー関数を使用して複雑なコンポーネントの状態を管理し、「ドリル」せずにネストされたコンポーネントに状態を転送します
- 戻ってきた
- レデューサー機能で非常に複雑な状態を制御する
- 状態が時間とともに変化した時期、理由、方法のトレーサビリティ
- 状態管理ロジックをUIレイヤーから完全に分離したい
- 異なるUIレイヤー間で状態管理ロジックを配布する
- ミドルウェアの機能を使用して、操作を初期化するときに追加のロジックを実装する
- 状態の特定の部分を保存する機能
- 再現可能なエラーレポートを取得する機能
- 開発中にロジックとUIをすばやくデバッグする機能
- Redux + React-Redux
- Reduxのすべてのユースケース+ ReactコンポーネントがReduxストアと対話する機能
もう一度:名前付きツールはさまざまな問題を解決します!
推奨事項
では、何を使用するかをどのように決定しますか?
これを行うには、アプリケーションの問題を最もよく解決するツールを決定する必要があります。
- 「ドリル」を避けたいだけの場合は、コンテキストを使用してください
- , , + useReducer()
- , , .., Redux + React-Redux
アプリケーションに状態を管理するための2〜3のコンテキストがある場合は、Reduxに切り替える必要があると思います。
「Reduxを使用するには多くの定型コードを書く必要がある」とよく耳にしますが、「最新のRedux」を使用すると、習得と使用がはるかに簡単になります。公式の ReduxToolkitはテンプレートの問題を解決し、React-Reduxフック はReactコンポーネントでReduxを簡単に使用できるようにします。
もちろん、依存関係としてRTKとReact-Reduxを追加すると、組み込みのコンテキスト+ useReducer()よりもアプリケーションバンドルが増加します。しかし、このアプローチの利点は、欠点をカバーしています-より良い状態トレース、より単純でより予測可能なロジック、改善されたコンポーネントレンダリングの最適化。
一方が他方を除外しないことに注意することも重要です-Redux、Context、およびuseReducer()を一緒に使用できます。「グローバル」状態をReduxに、ローカル状態をコンポーネントに保存することをお勧め します。アプリケーションのどの部分をReduxに保存し、どの部分をコンポーネントに保存するかを決定するときは注意が必要 です。..。したがって、Reduxを使用してグローバル状態を保存し、Context + useReducer()を使用してローカル状態を保存し、Contextを使用して静的な値をすべて同じアプリケーションに同時に保存できます。
繰り返しになりますが、すべてのアプリケーションの状態をReduxに保存する必要がある、またはReduxが常に最良のソリューションであると主張しているわけではありません。私のポイントは、Reduxは良い選択であり、Reduxを使用する理由はたくさんあり、使用料は多くの人が考えるほど高くはないということです。
最後に、contextとReduxは他に類を見ないものではありません。状態管理の他の側面に対処する他の多くのツールがあります。MobXは、OOPとオブザーバブルを使用して依存関係を自動的に更新する一般的なソリューションです。州の更新に対する他のアプローチには、Jotai、Recoil、およびZustandが含まれます。React Query、SWR、Apollo、Urqlなどのデータライブラリは、サーバーキャッシュ状態を操作するための一般的なパターンを簡単に使用できるようにする抽象化を提供します(同様のライブラリ(RTKクエリ)がRedux Toolkitにまもなく 登場します)。
この記事が、コンテキストとReduxの違い、およびどのツールをいつ使用すべきかを理解するのに役立つことを願っています。清聴ありがとうございました。