Relayにおける思考法
Relayのデータ取得手法は、Reactでの経験から大きな影響を受けています。特に、Reactは複雑なインターフェースを再利用可能な**コンポーネント**に分割することで、開発者がアプリケーションの個々のユニットを分離して考え、アプリケーションの異なる部分間の結合を軽減できるようにします。さらに重要なのは、これらのコンポーネントが**宣言的**であることです。開発者は、特定の状態に対してUIが*どのように*見えるべきかを指定するだけで、そのUIを*どのように*表示するかを気にする必要はありません。ネイティブビュー(例:DOM)を操作するために命令型コマンドを使用していた以前のアプローチとは異なり、ReactはUI記述を使用して必要なコマンドを自動的に決定します。
これらのアイデアをRelayにどのように組み込んだかを理解するために、いくつかの製品ユースケースを見てみましょう。Reactの基本的な知識があることを前提とします。
ビューのデータ取得
私たちの経験では、製品の大多数は、ローディングインジケーターを表示しながらビュー階層のすべてのデータを取得し、データが利用可能になったらビュー全体をレンダリングするという、1つの特定の動作を望んでいます。
1つの解決策は、ルートコンポーネントに、それ自体とそのすべての子コンポーネントに必要なデータを宣言して取得させることです。しかし、これは結合をもたらします。子コンポーネントへの変更は、それをレンダリングする可能性のあるルートコンポーネントの変更を必要とします!この結合は、バグの可能性が高くなり、開発ペースが遅くなることを意味する可能性があります。
もう1つの論理的なアプローチは、各コンポーネントに必要なデータを宣言して取得させることです。これは素晴らしいことのように聞こえます。しかし、問題は、コンポーネントが受信したデータに基づいて異なる子をレンダリングする可能性があることです。そのため、ネストされたコンポーネントは、親コンポーネントのクエリが完了するまで、データをレンダリングして取得を開始できません。言い換えれば、*これはデータ取得を段階的に強制します。*最初にルートをレンダリングして必要なデータを取得し、次にその子をレンダリングしてそのデータを取得します。リーフコンポーネントに到達するまで、これが続きます。レンダリングには、複数回の低速のシリアルラウンドトリップが必要になります。
Relayは、コンポーネントが必要なデータを指定できるようにすることで、両方のアプローチの利点を組み合わせますが、これらの要件をコンポーネントのサブツリー全体のデータを取得する単一のクエリに統合します。言い換えれば、ビュー全体の要件を*静的*に(つまり、アプリケーションを実行する前、コードを書く時点で)決定します!
これはGraphQLの助けを借りて実現されます。関数コンポーネントは、1つ以上のGraphQL フラグメントを使用してデータ要件を記述します。これらのフラグメントは、他のフラグメント内にネストされ、最終的にはクエリ内にネストされます。このようなクエリがフェッチされると、Relayはそのクエリとそのすべてのネストされたフラグメントに対して単一のネットワークリクエストを行います。言い換えれば、Relayランタイムは、ビューに必要なすべてのデータに対して*単一のネットワークリクエスト*を行うことができます!
Relayがどのようにこの偉業を達成するかを深く掘り下げてみましょう。
コンポーネントのデータ要件の指定
Relayでは、コンポーネントのデータ要件はフラグメントで指定されます。フラグメントは、特定のタイプのオブジェクトからどのフィールドを選択するかを指定する、名前付きのGraphQLスニペットです。フラグメントはGraphQLリテラル内に記述されます。たとえば、以下は、著者の名前と写真のURLを選択するフラグメントを含むGraphQLリテラルを宣言しています。
// AuthorDetails.react.js
const authorDetailsFragment = graphql`
fragment AuthorDetails_author on Author {
name
photo {
url
}
}
`;
このデータは、関数型ReactコンポーネントでuseFragment(...)
フックを呼び出すことによってストアから読み取られます。このデータを読み取る実際の著者は、useFragment
に渡される2番目のパラメーターによって決定されます。例えば
// AuthorDetails.react.js
export default function AuthorDetails(props) {
const data = useFragment(authorDetailsFragment, props.author);
// ...
}
この2番目のパラメーター(props.author
)はフラグメント参照です。フラグメント参照は、フラグメントを別のフラグメントまたはクエリに**スプレッド**することによって取得されます。フラグメントを直接フェッチすることはできません。代わりに、すべてのフラグメントは、データをフェッチするために最終的にクエリにスプレッド(直接または推移的に)する必要があります。
そのようなクエリの1つを見てみましょう。
クエリ
そのデータを取得するために、AuthorDetails_author
を次のようにスプレッドするクエリを宣言するかもしれません
// Story.react.js
const storyQuery = graphql`
query StoryQuery($storyID: ID!) {
story(id: $storyID) {
title
author {
...AuthorDetails_author
}
}
}
`;
これで、const data = useLazyLoadQuery(storyQuery, {storyID})
を呼び出すことによってクエリをフェッチできます。この時点で、data.story.author
(存在する場合。すべてのフィールドはデフォルトでnull許容です)は、AuthorDetails
に渡すことができるフラグメント参照になります。例えば
// Story.react.js
function Story(props) {
const data = useLazyLoadQuery(storyQuery, props.storyId);
return (<>
<Heading>{data?.story.title}</Heading>
{data?.story?.author && <AuthorDetails author={data.story.author} />}
</>);
}
ここで何が起こったかに注目してください。*Story
コンポーネント*と*AuthorDetails
コンポーネントの両方*に必要なデータを含む単一のネットワークリクエストを行いました!そのデータが利用可能になると、ビュー全体を1回のパスでレンダリングできました。
データマスキング
データ取得の典型的なアプローチでは、2つのコンポーネントが*暗黙的な依存関係*を持つことがよくあることがわかりました。たとえば、<Story />
は、データがフェッチされたことを直接確認せずに、いくつかのデータを使用する可能性があります。このデータは、多くの場合、<AuthorDetails />
などのシステムの他の部分によってフェッチされます。その後、<AuthorDetails />
を変更してそのデータ取得ロジックを削除すると、<Story />
が突然不可解に壊れることがあります。この種のバグは、常にすぐに明らかになるわけではなく、特に大規模なチームによって開発された大規模なアプリケーションではそうではありません。手動および自動テストはそれほど役に立ちません。これはまさに、フレームワークによって解決するのがより良いタイプの体系的な問題です。
Relayは、ビューのデータが一度にすべてフェッチされることを保証することを確認しました。しかし、Relayは、すぐに明らかにならない別の利点も提供します。**データマスキング**です。Relayは、コンポーネントがGraphQLフラグメントで具体的に要求したデータにのみアクセスすることを許可し、それ以外はアクセスできません。そのため、あるコンポーネントがストーリーのtitle
をクエリし、別のコンポーネントがtext
をクエリする場合、それぞれが要求したフィールド*のみ*を見ることができます。実際、コンポーネントは*子*によって要求されたデータを見ることさえできません。それもカプセル化を壊してしまいます。
Relayはさらに進んで、props
に不透明な識別子を使用して、コンポーネントをレンダリングする前にデータを明示的にフェッチしたことを検証します。<Story />
が<AuthorDetails />
をレンダリングするが、フラグメントをスプレッドするのを忘れた場合、Relayは<AuthorDetails />
のデータが欠落していることを警告します。実際、Relayは、他のコンポーネントがたまたま<AuthorDetails />
によって要求されたのと同じデータをフェッチした場合でも警告します。この警告は、今はうまくいく*かも*しれませんが、後で壊れる可能性が非常に高いことを示しています。
結論
GraphQLは、効率的で疎結合のクライアントアプリケーションを構築するための強力なツールを提供します。Relayはこの機能に基づいて構築され、**宣言型データフェッチ**のフレームワークを提供します。*何を*フェッチするかを*どのように*フェッチするかから分離することで、Relayは開発者がデフォルトで堅牢で透過的でパフォーマンスの高いアプリケーションを構築するのに役立ちます。Reactによって支持されているコンポーネント中心の考え方に対する素晴らしい補完です。これらのテクノロジー(React、Relay、GraphQL)はそれぞれ単独で強力ですが、この組み合わせは、*迅速に移動*し、*高品質のアプリを大規模に提供*できる**UIプラットフォーム**です。
このページは役に立ちましたか?
サイトをさらに良くするためにご協力ください いくつかの簡単な質問にお答えください.