Redux FAQ: 設計上の決定事項
目次
- Reduxが状態とアクションをサブスクライバーに渡さないのはなぜですか?
- ReduxがアクションとReducerにクラスの使用をサポートしないのはなぜですか?
- ミドルウェアのシグネチャがカリー化を使用するのはなぜですか?
- applyMiddlewareがdispatchに対してクロージャを使用するのはなぜですか?
combineReducers
が各Reducerを呼び出す際に、全体の状態を含む第3引数を含まないのはなぜですか?mapDispatchToProps
がgetState()
またはmapStateToProps()
からの戻り値の使用を許可しないのはなぜですか?
設計上の決定事項
Reduxが状態とアクションをサブスクライバーに渡さないのはなぜですか?
サブスクライバーは、アクションではなく、状態値自体に対応することを意図しています。状態の更新は同期的に処理されますが、サブスクライバーへの通知はバッチ処理またはデバウンスされる可能性があり、つまり、すべてのアクションでサブスクライバーに通知されるとは限りません。これは、繰り返しレンダリングを回避するための一般的なパフォーマンス最適化です。
バッチ処理またはデバウンスは、エンハンサーを使用してstore.dispatch
をオーバーライドし、サブスクライバーに通知する方法を変更することで可能です。また、パフォーマンスを最適化し、繰り返しレンダリングを回避するために、アクションをバッチで処理するようにReduxを変更するライブラリもあります。
- redux-batchでは、1つの通知だけで、アクションの配列を
store.dispatch()
に渡すことができます。 - redux-batched-subscribeでは、ディスパッチの結果として発生するサブスクライブ通知のバッチ処理が可能です。
意図された保証は、Reduxが最終的に最新の利用可能な状態ですべてのサブスクライバーを呼び出すことですが、各アクションごとに各サブスクライバーを常に呼び出すことではありません。ストアの状態は、単にstore.getState()
を呼び出すことで、サブスクライバーで利用できます。アクションをバッチ処理する方法を壊すことなく、サブスクライバーでアクションを利用可能にすることはできません。
サブスクライバー内でアクションを使用すること(サポートされていない機能)の潜在的なユースケースは、特定の種類のアクションの後でのみコンポーネントが再レンダリングされるようにすることです。代わりに、再レンダリングは以下で制御する必要があります。
- shouldComponentUpdateライフサイクルメソッド
- 仮想DOMの等価性チェック(vDOMEq)
- React.PureComponent
- React-Reduxを使用する:必要なストアの部分にのみコンポーネントをサブスクライブするには、mapStateToPropsを使用します。
詳細情報
記事
ディスカッション
ReduxがアクションとReducerにクラスの使用をサポートしないのはなぜですか?
アクションクリエーターと呼ばれる関数を使用してアクションオブジェクトを返すパターンは、多くのオブジェクト指向プログラミングの経験を持つプログラマーには直感に反するように見えるかもしれません。彼らはこれをクラスとインスタンスの強力なユースケースだと考えるでしょう。アクションオブジェクトとReducerのクラスインスタンスは、クラスインスタンスがシリアル化とデシリアル化を難しくするため、サポートされていません。JSON.parse(string)
などのデシリアル化メソッドは、クラスインスタンスではなく、プレーンなJavaScriptオブジェクトを返します。
ストアのFAQで説明されているように、永続化やタイムトラベルデバッグが意図したとおりに動作しなくても問題ない場合は、シリアル化できないアイテムをReduxストアに入れることができます。
シリアル化により、ブラウザはディスパッチされたすべてのアクションと以前のストアの状態を、はるかに少ないメモリで保存できます。ストアの巻き戻しと「ホットリロード」は、Redux開発者のエクスペリエンスとRedux DevToolsの機能の中核です。これにより、デシリアル化されたアクションをサーバーに保存し、Reduxによるサーバーサイドレンダリングの場合にブラウザで再シリアル化することもできます。
詳細情報
記事
ディスカッション
ミドルウェアのシグネチャがカリー化を使用するのはなぜですか?
Reduxミドルウェアは、const middleware = storeAPI => next => action => {}
のように見える三重にネストされた関数構造を使用して記述され、const middleware = (storeAPI, next, action) => {}
のように見える単一関数ではなく記述されます。これにはいくつかの理由があります。
1つは、「カリー化」関数は標準的な関数型プログラミング手法であり、Reduxは設計において明示的に関数型プログラミングの原則を使用することを意図していたことです。もう1つは、カリー化関数が、ミドルウェアの存続期間中に存在する変数を宣言できるクロージャを作成することです(これは、クラスインスタンスの存続期間中に存在するインスタンス変数の関数的な等価物と見なすことができます)。最後に、それは単にReduxが最初に設計されたときに選択されたアプローチです。
ミドルウェアの宣言におけるカリー化された関数シグネチャは、一部の人々によって不要とみなされています。なぜなら、applyMiddleware関数が実行されると、ストアとnextの両方が利用可能になるからです。この問題は、Reduxエコシステムには現在、既存のミドルウェア定義に依存する何百ものミドルウェアが存在するため、破壊的な変更を導入する価値がないと判断されています。
詳細情報
ディスカッション
- ミドルウェアのシグネチャがカリー化を使用するのはなぜですか?
applyMiddleware
がdispatch
に対してクロージャを使用するのはなぜですか?
applyMiddleware
は、ストアから既存のdispatchを取得し、それをクローズして、getStateとdispatch関数を公開するオブジェクトを使用して呼び出されたミドルウェアの初期チェーンを作成します。これにより、初期化中にdispatchに依存するミドルウェアを実行できます。
詳細情報
ディスカッション
combineReducers
が各Reducerを呼び出す際に、全体の状態を含む第3引数を含まないのはなぜですか?
combineReducers
は、ドメイン別にReducerロジックを分割することを推奨するよう意図的に設計されています。combineReducers
を超えてで述べられているように、combineReducers
は、特定のスライスReducerに状態のスライスの更新作業を委任することで、プレーンなJavaScriptオブジェクトである状態ツリーを更新するという単一の一般的なユースケースを処理するように意図的に制限されています。
各Reducerへの潜在的な第3引数が何であるかは、すぐに明らかではありません。全体の状態ツリー、何らかのコールバック関数、状態ツリーの他の部分などです。combineReducers
がユースケースに合わない場合は、深くネストされたReducerやグローバル状態へのアクセスを必要とするReducerに対して、combineSectionReducersやreduceReducersなどのライブラリを使用することを検討してください。
公開されているユーティリティのいずれもユースケースを解決しない場合は、必要な機能を正確に実行する関数を自分で記述できます。
詳細情報
記事
ディスカッション
mapDispatchToProps
がgetState()
またはmapStateToProps()
からの戻り値の使用を許可しないのはなぜですか?
mapDispatch
内で関数が宣言されると、ストアから返された最新の値をクローズオーバーできるように、mapDispatch
内で全体の状態またはmapState
の戻り値を使用する要求がありました。
このアプローチはmapDispatch
ではサポートされていません。なぜなら、ストアが更新されるたびにmapDispatch
も呼び出す必要があるからです。これにより、状態の更新ごとに関数が再作成されるため、パフォーマンスのオーバーヘッドが大幅に増加します。
現在の状態と`mapDispatchToProps`関数に基づいてpropsを変更する必要があるというユースケースを処理する推奨方法は、`connect`関数の第3引数である`mergeProps`を使用することです。指定された場合、`mapStateToProps()`、`mapDispatchToProps()`、およびコンテナコンポーネントのpropsの結果が渡されます。`mergeProps`から返されるプレーンオブジェクトは、ラップされたコンポーネントにpropsとして渡されます。
詳細情報
ディスカッション