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

Redux FAQ: アクション

目次

アクション

なぜ type は文字列であるべきなのか?なぜアクションタイプは定数であるべきなのか?

状態と同様に、シリアライズ可能なアクションは、タイムトラベルデバッグやアクションの記録と再生など、Redux の定義的な機能のいくつかを実現します。type 値に Symbol のようなものを使用したり、アクション自体に instanceof チェックを使用したりすると、それが壊れてしまいます。文字列はシリアライズ可能で、簡単に自己記述できるため、より良い選択です。アクションがミドルウェアで使用されることを意図している場合、アクションで Symbols、Promises、またはその他のシリアライズ不可能な値を使用しても*問題ない*ことに注意してください。アクションは、実際にストアに到達し、リデューサーに渡されるまでにシリアライズ可能であるだけでよいのです。

パフォーマンス上の理由から、シリアライズ可能なアクションを確実に強制することはできないため、Redux はすべてのアクションがプレーンなオブジェクトであることと、type が文字列であることをチェックするだけです。残りはあなた次第ですが、すべてをシリアライズ可能にしておくと、デバッグや問題の再現に役立つ可能性があります。

一般的に使用されるコードをカプセル化して集中化することは、プログラミングにおける重要な概念です。どこでも手動でアクションオブジェクトを作成し、各 type 値を手書きすることは確かに可能ですが、再利用可能な定数を定義すると、コードのメンテナンスが容易になります。定数を別のファイルに配置すると、import ステートメントのスペルミスをチェックして、誤った文字列を誤って使用しないようにすることができます。

さらに詳しい情報

ドキュメント

議論

リデューサーとアクションの間には常に一対一のマッピングがあるのか?

いいえ。ステートの特定のスライスへの更新を担当する、独立した小さなリデューサー関数を作成することをお勧めします。このパターンを「リデューサー合成」と呼びます。特定のアクションは、すべて、一部、またはどれも処理しない可能性があります。これにより、コンポーネントは実際のデータ変更から分離されます。1つのアクションが状態ツリーの異なる部分に影響を与える可能性があり、コンポーネントがこれを認識する必要がないためです。一部のユーザーは、「ダックス」ファイル構造のように、それらをより緊密にバインドすることを選択しますが、デフォルトでは一対一のマッピングは間違いなくなく、多くの場合にリデューサーでアクションを処理したい場合は、そのようなパラダイムを破る必要があります。

さらに詳しい情報

ドキュメント

ディスカッション

AJAX 呼び出しなどの「副作用」をどのように表現できるのか?なぜ非同期処理を行うために「アクションクリエーター」、「サンク」、「ミドルウェア」のようなものが必要なのか?

これは長く複雑なトピックであり、コードをどのように整理し、どのようなアプローチを使用すべきかについてさまざまな意見があります。

意味のある Web アプリは、通常、AJAX リクエストを行うなどの非同期作業を含む複雑なロジックを実行する必要があります。そのコードはもはや純粋に入力の関数ではなくなり、外部世界との相互作用は 「副作用」として知られています。

Redux は関数型プログラミングに触発されており、そのままの状態では副作用を実行する場所がありません。特に、リデューサー関数は常に (state, action) => newState の純粋関数で*なければなりません*。ただし、Redux のミドルウェアを使用すると、ディスパッチされたアクションをインターセプトし、副作用を含む追加の複雑な動作をアクションに追加することができます。

一般的に、Redux では、副作用のあるコードはアクション作成プロセスの一部であるべきであると考えています。そのロジックは UI コンポーネント内で実行できますが、通常は複数の場所から同じロジックを呼び出すことができるように、そのロジックを再利用可能な関数(つまり、アクションクリエーター関数)に抽出するのが理にかなっています。

これを行う最も簡単で一般的な方法は、Redux Thunk ミドルウェアを追加することです。これにより、より複雑な非同期ロジックを持つアクションクリエーターを記述できます。広く使用されている別の方法は Redux Saga で、ジェネレーターを使用してより同期的なコードを記述でき、Redux アプリで「バックグラウンドスレッド」または「デーモン」のように動作できます。さらに別のアプローチは Redux Loop で、状態の変化に応じて副作用を宣言し、それらを別々に実行できるようにすることでプロセスを反転させます。それ以外にも、副作用をどのように管理するかについて独自の見解を持つ、コミュニティで開発された*多数の*ライブラリやアイデアがあります。

さらに詳しい情報

ドキュメント

記事

ディスカッション

どの非同期ミドルウェアを使うべきか?サンク、サガ、オブザーバブル、またはその他のものの中からどのように選択するのか?

多数の非同期/副作用ミドルウェアが利用可能ですが、最も一般的に使用されているのは redux-thunkredux-saga、および redux-observable です。これらは異なるツールであり、それぞれ長所、短所、およびユースケースが異なります。

一般的な経験則として

  • サンクは、複雑な同期ロジック(特に Redux ストアの状態全体へのアクセスを必要とするコード)と、単純な非同期ロジック(基本的な AJAX 呼び出しなど)に最適です。async/await を使用すると、サンクをより複雑な Promise ベースのロジックにも使用するのが合理的になります。
  • サガは、複雑な非同期ロジックと、切り離された「バックグラウンドスレッド」タイプの動作に最適です。特に、ディスパッチされたアクションをリッスンする必要がある場合は(これはサンクでは実行できないことです)。これらを使用するには、ジェネレーター関数と redux-saga の「エフェクト」演算子に精通している必要があります。
  • オブザーバブルはサガと同じ問題を解決しますが、非同期動作を実装するために RxJS に依存しています。これらを使用するには、RxJS API に精通している必要があります。

ほとんどの Redux ユーザーは、まずサンクから始め、アプリでより複雑な非同期ロジックの処理が本当に必要な場合は、後でサガやオブザーバブルなどの追加の副作用ライブラリを追加することをお勧めします。

サガとオブザーバブルには同じユースケースがあるため、アプリケーションは通常、どちらか一方を使用しますが、両方は使用しません。ただし、サンクとサガまたはオブザーバブルの両方を一緒に使用してもまったく問題ありません。これらは異なる問題を解決するためです。

記事

ディスカッション

1つのアクションクリエーターから複数のアクションを連続してディスパッチするべきか?

アクションをどのように構成する必要があるかについての特定のルールはありません。Redux Thunk のような非同期ミドルウェアを使用すると、複数の個別の関連アクションを連続してディスパッチしたり、AJAX リクエストの進行状況を表すアクションをディスパッチしたり、状態に基づいて条件付きでアクションをディスパッチしたり、アクションをディスパッチしてその直後に更新された状態を確認したりするなどのシナリオが確実に可能になります。

一般的に、これらのアクションが関連しているが独立しているのか、それとも実際には1つのアクションとして表現すべきなのかを自問してください。ご自身の状況に合わせて判断するのが良いですが、リデューサーの可読性とアクションログの可読性のバランスを取るようにしてください。例えば、新しい状態ツリー全体を含むアクションは、リデューサーを1行にするでしょう。しかし、欠点は、変更がなぜ発生しているかの履歴がなくなるため、デバッグが非常に困難になることです。一方で、アクションを細かく保つためにループでアクションを発行する場合、それは異なる方法で処理される新しいアクションタイプを導入したい兆候かもしれません。

パフォーマンスが気になる箇所で、同期的に連続して複数回ディスパッチすることを避けてください。ディスパッチをまとめてバッチ処理できるアドオンやアプローチが多数存在します。

詳細情報

ドキュメント

記事

ディスカッション