みなさんこんにちは! Peggy RayzisによるReact ApolloによるReduxコードの削減という興味深い記事の翻訳を共有したいと思います。 著者と彼女のチームがどのように彼らのプロジェクトでGraphQLテクノロジーを実装したかについての記事。 翻訳は著者の許可を得て公開されています。
Higher Football Leagueでの宣言型アプローチへの切り替え
私は、最高のコードはコードの欠如だと強く信じています。 コードが多いほど、バグが発生する可能性が高くなり、そのようなコードを維持するためにより多くの時間が費やされます。 ハイアーフットボールリーグでは、チームが非常に小さいため、この原則を重視しています。 コードの再利用を増やすか、単にコードの特定の部分のサービスを停止することにより、可能な限りすべてを最適化しようとします。
この記事では、データの受信に対する制御をApolloに転送し、約5,000行のコードを削除する方法について学習します 。 さらに、Apolloに切り替えた後、アプリケーションは必要なデータのみを要求するようになったため、アプリケーションのボリュームが非常に小さくなっただけでなく、より宣言的になりました。
宣言とはどういう意味ですか、なぜそれがとてもクールなのですか? 宣言型プログラミングは究極の目標に焦点を当てていますが、命令型プログラミングはそれを達成するために必要なステップに焦点を当てています。 React自体は宣言的です。
Reduxを使用したデータの取得
簡単なArticleコンポーネントを見てみましょう。
接続された<MatchDetail/>
ビューで<Article/>
をレンダリングし、一致IDを小道具として受け取るとします。 GraphQLクライアントなしでこれを行う場合、 <Article/>
レンダリングに必要なデータを取得するプロセスは次のようになります。
-
<MatchDetail/>
がマウントされたら、アクション作成者を呼び出してIDで一致データを取得します。 アクション作成者はアクションをディスパッチして、データ収集プロセスの開始をReduxに通知します。 - 目的地に到着し、データを返送しています。 便利な構造でデータを正規化します。
- データが正規化された後、データ取得プロセスの完了についてReduxに通知する別のアクションをディスパッチします。
- Reduxはリデューサーでアクションを処理し、アプリケーションの状態を更新します。
-
<MatchDetail/>
は、小道具を介して必要なすべての一致データを受け取り、それらをフィルタリングして記事をレンダリングします。
<Article/>
データを取得するだけの多くのステップ! GraphQLクライアントがないと、データを取得する方法に専念する必要があるため、コードがはるかに不可欠になります。 しかし、 <Article/>
単純なレンダリングのためにすべての一致データを転送したくない場合はどうでしょうか? 別のエンドポイントを構築し、そこからデータを受信するアクションクリエーターの別のセットを作成できますが、このオプションは非常に簡単にサポートされなくなる可能性があります。
GraphQLで同じことができる方法を比較してみましょう。
-
<MatchDetail/>
、次のリクエストを実行する高次コンポーネントに接続されています。
query Article($id: Float!) { match(id: $id) { article { title body } } }
...そしてそれだけです! クライアントがデータを受信するとすぐに、それらを小道具に渡し、さらに<Article/>
送信できます。 これは、コンポーネントをレンダリングするために必要なデータのみに焦点を合わせるため、はるかに宣言的です。
これは、RelayでもApolloでも、GraphQLデータの受信をクライアントに委任することの利点です。 「 GraphQLの概念 」を考え始めると、このデータを取得する方法を心配するのではなく、コンポーネントがレンダリングする必要がある小道具だけを気にすることになります。
ある時点でこの「 方法 」について考える必要がありますが、これはすでにサーバー側の懸念事項であり、したがって、フロントエンドの複雑さが大幅に軽減されます。 GraphQLサーバーアーキテクチャを初めて使用する場合は、Apolloライブラリであるgraphql-tools
試してください。これにより、スキーマをよりgraphql-tools
的に構成できます。 簡潔にするために、今日はフロントエンド部分のみに焦点を当てます。
この投稿ではReduxコードの使用を削減する方法について説明していますが、それを完全になくすことはできません。 Apolloはボンネットの下でReduxを使用しているので、不変性の恩恵を受けることができ、タイムトラベルデバッグなどのRedux Dev Toolsのすべてのクールな機能も機能します。 構成中に、 ApolloをReduxの既存のストアに接続して、単一の「真実の源」をサポートできます。 ストアを構成したら、アプリケーション全体をラップする<ApolloProvider/>
コンポーネントにストアを渡します。 おなじみの音? このコンポーネントは、クライアントプロパティを介してApolloClient
インスタンスを渡す必要があることを除き、Reduxの既存の<Provider/>
を完全に置き換えます。
Reduxコードのカットを開始する前に、GraphQLの最も機能的な機能の1つであるインクリメンタル採用に名前を付けます 。 アプリケーション全体を一度にリファクタリングする必要はありません。 Apolloを既存のReduxストアと統合した後、レデューサーから徐々に切り替えることができます。 サーバー側にも同じことが当てはまります。大規模なアプリケーションで作業している場合、完全な移行の準備ができるまで、現在のREST APIとGraphQLを併用できます。 公正な警告:GraphQLを試すとすぐに、このテクノロジーに夢中になり、アプリケーション全体を作り直したいと思うかもしれません。
私たちの要件
ReduxからApolloに移行する前に、Apolloがニーズを満たしているかどうかを慎重に検討しました。 決定する前に気づいたのは次のとおりです。
- 複数のソースからのデータの集約:一致は4つの異なるソースから取得したデータで構成されます:コンテンツ-REST APIから、統計-MySQLデータベースから、メディアデータ-ビデオAPIおよびソーシャルデータ-Redisストレージから。 最初に、サーバープラグインを使用して、クライアントに送信する前にすべてのデータを1つの一致オブジェクトに収集しました。 私は言わなければならない、それはGraphQLとほぼ同じように機能します! これに気付いたとき、GraphQLがアプリケーションの完璧な候補になることが明らかになりました。
- リアルタイムの運用上の更新:ライブマッチでは、通常1分ごとに更新を受け取ります。 Apolloの前に、ソケットを使用して更新を処理し、それらをマッチレデューサーにディスパッチしました。 これは最も恐ろしい決定ではありませんでしたが、複雑なシーケンスを避けるために一致のオブジェクト全体をディスパッチしたため、最もエレガントでもありませんでした。 Apolloを使用すると、ゲームのステータスに応じて、各コンポーネントのポーリング間隔を簡単にカスタマイズできます。
- シンプルなページネーション:マッチのリストを無限にスクロールするスケジュールページを作成したため、頭痛のないこの種のページネーションの適切な処理が必要でした。 もちろん、独自の減速機を作成することもできます。 しかし、Apolloが私たちのためにすべてのハードワークを行う
fetchMore
関数を持っているfetchMore
、なぜ自分でそれを書くのでしょうか?
Apolloは現在のすべての要件を満たしているだけでなく、特にパーソナライズがロードマップに含まれていることを考慮して、将来のニーズの一部もカバーしたと言わなければなりません。 現在、サーバーは読み取り専用ですが、ユーザーにお気に入りのチームを保存するために、将来的に突然変異を導入する必要がある場合があります。 ポーリングでは解決できないリアルタイムのコメントやファンとのやり取りを追加する場合、Apolloはサブスクリプションをサポートします 。
Reduxからアポロへ
みんなが待っていた瞬間! 最初は、この記事を書くことを考えていたとき、前後のコード例を紹介するだけでしたが、これら2つのアプローチを直接比較することは難しいと思います(特にApolloの初心者向け)。 代わりに、リモートコード全体の量を計算し、Apolloを使用してコンテナコンポーネントを作成するときに適用できるおなじみのReduxの概念をガイドします。
削除したもの
- マッチレデューサー(〜300行のコード)
- データの受信を担当するアクションクリエーターとエピック(〜800行のコード)
- ソケットを介して一致のライブ更新をグループ化および受信するアクションクリエーターおよびビジネスロジック(〜750行のコード)
- ローカルストレージを担当するアクションクリエーターとエピック(〜1000行のコード)。 実際、これまでのプロジェクトではオフラインサポートが延期されていたため、この項目を一般リストに含めることは完全に公平ではありませんが、再度追加したい場合は、Apolloの
fetchPolicy
関数をカスタマイズし、減速機。 - プレゼンテーションコンポーネントからReduxロジックを分離したReduxコンテナコンポーネント(〜1000行のコード)
- 上記のすべてに関連するテスト(〜1000行のコード)
接続()→graphql()
connect
使用方法を知っている場合、Apolloの高次graphql
は非常に馴染みのあるものになります。 connect
がコンポーネントを受け入れてReduxストアに接続する関数を返すように、 graphql
コンポーネントを受け入れてそれをApolloクライアントに「接続」する関数を返します。 実際に見てみましょう!
graphql
渡される最初の引数はMatchSummaryQuery
です。 これは、サーバーから受信するデータです。 Webpackローダーを使用してGraphQL ASTでクエリを解析しますが、Webpackを使用しない場合は、クエリをテンプレート文字列でラップし、Apolloからエクスポートされたgql
関数に渡す必要があります。 コンポーネントに必要なデータのリクエストの例を次に示します。
素晴らしい、リクエストがあります! 正しく実行するには、2つの変数$id
と$season
に渡す必要があります。 しかし、これらの変数はどこで取得できますか? ここで、 graphql
関数の2番目の引数が機能し、構成オブジェクトとして提示されます。
このオブジェクトには、高次コンポーネント(HOC)の動作を構成するために指定できるいくつかのプロパティがあります。 最も重要なプロパティの1つはoptions
で、コンテナの小道具を受け取る関数を受け取ります。 この関数は、変数をリクエストに渡すことができるタイプvariables
プロパティと、コンポーネントのポーリング動作をカスタマイズできるpollInterval
持つオブジェクトを返します。 id
とseason
をMatchSummaryQuery
に渡すためにコンテナの小道具をどのように使用するかに注目してMatchSummaryQuery
。 この関数が長くなりすぎてデコレータに直接書き込むことができない場合、 mapPropsToOptions
と呼ばれる別の関数にmapPropsToOptions
ます。
mapStateToProps()→mapResultsToProps()
ReduxコンテナーでmapStateToProps
関数を使用し、この関数を渡してconnect
して、状態アプリケーションからこのコンテナーの小道具にデータを転送しました。 Apolloでは、同様の関数を定義できます。 graphql
関数に以前に渡した構成オブジェクトを覚えていますか? このオブジェクトにはもう1つのプロパティがありますprops
、propsを入力として受け取り、コンテナに渡す前にそれらを処理する関数を受け取ります。 もちろん、 graphql
で直接定義できますが、別のmapResultsToProps
関数として定義しmapResultsToProps
です。
なぜ小道具を再定義する必要があるのですか? GraphQLクエリの結果は、常にdata
プロパティに割り当てられます。 このデータをコンポーネントに送信する前に調整する必要がある場合があります。 次に例を示します。
これで、データオブジェクトには、リクエストの結果だけでなく、 data.loading
タイプのプロパティも含まれ、リクエストがまだレスポンスを返していないことがわかります。 これは、 <NoDataSummary/>
行ったように、同様の状況で別のコンポーネントをユーザーに表示する場合に役立ちます。
作成()
ComposeはReduxで使用される機能だけではありませんが、Apolloに含まれているという事実に引き続き注意を向けたいと思います。 1つのコンテナで使用するために複数のgraphql
関数を構築したい場合に非常に便利です。 その中で、 graphql
とともにReduxのconnect
機能を使用することもできます! さまざまな一致状態を表示するために、 compose
を使用compose
方法は次のとおりです。
コンテナに複数の状態が含まれる場合、 compose
うまく機能します。 しかし、状態に応じてのみ別のリクエストを実行する必要がある場合はどうでしょうか? ここでskip
は、上記の構成オブジェクトで確認できるように役立ちます。 skip
プロパティは、propsを受け取る関数を受け入れ、必要な基準を満たさない場合はクエリの実行をスキップできます。
これらすべての例は、Reduxを知っていれば、すぐにApolloの開発に参加できることを示しています! そのAPIにはReduxの概念の多くが組み込まれていますが、同じ結果を達成するために記述する必要があるコードの量は削減されています。
メジャーリーグサッカーをアポロに移した経験があなたのお役に立てば幸いです! さまざまなライブラリに関する決定と同様に、アプリケーションでのデータの受信を制御する最適な決定は、プロジェクトの特定の要件によって異なります。 私たちの経験に関して質問がある場合は、 ここにコメントを残すか、 Twitterでフォローしてください!
読んでくれてありがとう!