メインコンテンツへスキップ

React Redux

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

Redux FAQ: React Redux

React-Reduxを使うべき理由は?

Redux自体は独立したライブラリであり、React、Angular、Vue、Ember、バニラJSなど、あらゆるUIレイヤーやフレームワークで使用できます。ReduxとReactは一般的に併用されますが、互いに独立した存在です。

Reduxを任意のUIフレームワークと組み合わせて使用する場合、通常は「UIバインディング」ライブラリを使用してReduxとUIフレームワークを連携させます。UIコードから直接ストアとやり取りすることは推奨されません。

React-ReduxはReact向けの公式Redux UIバインディングライブラリです。ReduxとReactを併用する場合は、これら2つのライブラリを連携させるためにReact-Reduxも使用するべきです。

Reduxストアのサブスクリプションロジックを手動で実装することも可能ですが、その場合コードは非常に冗長になります。さらに、UIパフォーマンスの最適化には複雑なロジックが必要となります。

ストアの購読、更新データの確認、再レンダリングのトリガーといったプロセスは、より汎用的で再利用可能な形に抽象化できます。React-ReduxのようなUIバインディングライブラリは、ストアとのやり取りロジックを処理するため、このコードを自前で実装する必要がありません。

全体的に見て、React-Reduxは適切なReactアーキテクチャを促進し、複雑なパフォーマンス最適化を実装してくれます。またReduxとReactの最新API変更にも常に対応しています。

参考情報

ドキュメント

コンポーネントが再レンダリングされない、またはmapStateToPropsが実行されないのはなぜ?

意図せずステートを直接変更してしまうことが、アクションがディスパッチされた後にコンポーネントが再レンダリングされない最も一般的な原因です。Reduxはリデューサーがステートを「イミュータブル(不変)」に更新することを想定しており、これは常にデータのコピーを作成し、変更をそのコピーに適用することを意味します。リデューサーから同じオブジェクトを返した場合、たとえ内容を変更していたとしても、Reduxは何も変更されていないと判断します。同様にReact Reduxは、shouldComponentUpdateで受け取るpropsの参照を浅く比較するパフォーマンス最適化を行っており、すべての参照が同じ場合、shouldComponentUpdatefalseを返して元のコンポーネントの更新をスキップします。

ネストされた値を更新する際には、ステートツリー内でその上位にあるすべての要素の新しいコピーを返す必要があることを覚えておくことが重要です。state.a.b.c.dがある状態でdを更新したい場合、cba、そしてstate自体の新しいコピーも返す必要があります。このステートツリー変更図は、ツリーの深い部分での変更が上層まで連鎖的に影響する様子を示しています。

「データをイミュータブルに更新する」ことは、必ずしもImmerを使用しなければならないという意味では_ありません_(もちろんそれは選択肢の一つです)。以下のような複数の方法で、プレーンなJSオブジェクトや配列のイミュータブルな更新が可能です:

  • Object.assign()_.extend()などのオブジェクトコピー関数、slice()concat()などの配列関数の使用

  • ES2015の配列スプレッド演算子、ES2018のオブジェクトスプレッド演算子

  • イミュータブルな更新ロジックをシンプルな関数にラップするユーティリティライブラリ

参考情報

ドキュメント

記事

ディスカッション

コンポーネントが過剰に再レンダリングされるのはなぜですか?

React Reduxは、コンポーネントが実際に必要な場合のみ再レンダリングされるよう複数の最適化を実装しています。その一つが、connectに渡されるmapStateToPropsmapDispatchToPropsから生成されるpropsオブジェクトに対する浅い比較(shallow equality)チェックです。しかしmapStateToPropsが呼び出されるたびに新しい配列やオブジェクトインスタンスが生成される場合、浅い比較では不十分です。典型的な例はID配列をマッピングして対応するオブジェクト参照を返すケースです:

const mapStateToProps = state => {
return {
objects: state.objectIds.map(id => state.objects[id])
}
}

配列内のオブジェクト参照が同じでも配列自体の参照が異なるため、浅い比較チェックが失敗し、React Reduxはラップされたコンポーネントを再レンダリングします。

余分な再レンダリングは、レデューサーでオブジェクト配列を状態に保存する、Reselectでマッピング結果をキャッシュする、コンポーネントでshouldComponentUpdateを手動実装し_.isEqualのような関数で詳細なprops比較を行うことで解決できます。ただしカスタムshouldComponentUpdate()のコストがレンダリング自体を上回らないよう注意し、必ずプロファイラーでパフォーマンス仮説を検証してください。

非接続コンポーネントの場合、渡されるpropsを確認してください。親コンポーネントがrender関数内で<Child onClick={this.handleClick.bind(this)} />のようにコールバックを再バインドすると、親の再レンダリングごに関数参照が新しくなる一般的な問題があります。コールバックは親コンポーネントのコンストラクタで一度だけバインドするのがベストプラクティスです。

参考情報

ドキュメント

記事

ディスカッション

ライブラリ

mapStateToProps の処理を高速化するにはどうすればよいですか?

React Redux は mapStateToProps 関数が呼び出される回数を最小限に抑えるよう努めていますが、それでも mapStateToProps の実行速度を速め、処理量を最小化することが重要です。一般的に推奨されるアプローチは、Reselect を使用してメモ化された「セレクター」関数を作成することです。これらのセレクターは組み合わせてパイプライン化でき、後続のセレクターは入力が変更された場合にのみ実行されます。これにより、フィルタリングやソートなどの処理を行うセレクターを作成し、必要な時だけ実際の処理が行われるように保証できます。

参考情報

ドキュメント

記事

ディスカッション

接続されたコンポーネントで this.props.dispatch が利用できないのはなぜですか?

connect() 関数は2つの主要な引数を取ります(いずれもオプション)。1つ目の mapStateToProps は、ストアからデータを取得してコンポーネントにプロパティとして渡す関数です。2つ目の mapDispatchToProps は、ストアの dispatch 関数を活用するための関数で、通常はアクションクリエーターを事前にバインドし、呼び出されると自動的にアクションをディスパッチします。

connect() 呼び出し時に独自の mapDispatchToProps 関数を提供しない場合、React Redux はデフォルトバージョンを提供し、dispatch 関数をプロパティとして返します。独自の関数を提供すると、dispatch は自動的には提供されません。プロパティとして利用可能にするには、mapDispatchToProps 実装内で明示的に返す必要があります。

参考情報

ドキュメント

ディスカッション

トップコンポーネントだけを接続すべきですか?それともツリー内の複数コンポーネントを接続できますか?

初期のReduxドキュメントでは、コンポーネントツリーの上部近くにある少数のコンポーネントだけを接続するよう推奨していました。しかし、時間と経験から、このようなアーキテクチャでは上位コンポーネントが子孫のデータ要件を過剰に把握する必要があり、混乱を招く数のプロパティを下位に渡すことが判明しました。

現在推奨されるベストプラクティスは、コンポーネントを「プレゼンテーション」コンポーネントと「コンテナ」コンポーネントに分類し、適切な場所で接続されたコンテナコンポーネントを抽出することです:

「トップに1つのコンテナコンポーネント」を強調するReduxの例は誤りでした。これを原則としないでください。プレゼンテーションコンポーネントは分離させてください。便利なときに接続してコンテナコンポーネントを作成します。同じ種類の子コンポーネントにデータを提供するために親コンポーネントでコードの重複が発生していると感じたら、コンテナを抽出する時です。一般的に、親が子の「個人的な」データやアクションを過度に把握していると感じたら、コンテナを抽出する時です。

実際、ベンチマークでは、接続されたコンポーネントが多いほど、少ない場合よりもパフォーマンスが向上することが示されています。

一般的に、コンポーネント間で理解しやすいデータフローと責任範囲のバランスを見つけるようにしてください。

参考情報

ドキュメント

記事

ディスカッション

Redux と React Context API はどう比較されますか?

類似点

Redux と React の Context API はどちらも「プロップドリリング」の問題に対処します。両者ともプロパティを複数のコンポーネント層に渡さずにデータをやり取りできます。内部的には、Redux は React の Context API を利用してストアをコンポーネントツリーに伝播させています。

相違点

Redux を使用すると、Redux DevTools Extension の強力な機能が利用できます。これはアプリの全アクションを自動記録し、タイムトラベリング(過去の任意のアクションをクリックしてその時点の状態に戻る機能)を可能にします。また Redux はミドルウェアをサポートしており、各アクション dispatch 時にカスタム関数を実行できます(自動ロギングや特定アクションのインターセプトなど)。

React の Context API では、一対のコンポーネント間でのみ通信が行われます。これにより無関係なデータ間の適切な分離が実現されます。また親コンポーネントの状態を提供したり、コンテキストデータをラップされたコンポーネントにプロパティとして渡す柔軟性もあります。

Redux と React Context の根本的な違いはデータの扱い方にあります。Redux はアプリ全体の状態を単一の巨大なステートオブジェクトとして管理し、提供された reducer 関数を通じてデータ変更を推論します。React Redux はコンポーネントのレンダリングを最適化し、必要なデータが変更された場合のみ再レンダリングします。一方 Context は状態を保持せず単なるデータ伝達路であり、データ変更を表現するには親コンポーネントの状態に依存する必要があります。

参考情報