メインコンテンツにスキップ
バージョン: v18.0.0

クエリ

GraphQLクエリとは、GraphQLサーバーからクエリしたいデータの記述のことです。これは、GraphQLサーバーにリクエストしたい一連のフィールド(および潜在的にフラグメント)で構成されます。クエリできる内容は、サーバーで公開されているGraphQLスキーマによって異なります。これは、クエリに利用できるデータを記述します。

クエリは、データのフェッチのために、クエリが使用するオプションの変数のコレクションとともに、ネットワークを介してリクエストとして送信できます。サーバーのレスポンスは、送信したクエリの形状と一致するJSONオブジェクトになります。

query UserQuery($id: ID!) {
user(id: $id) {
id
name
...UserFragment
}
viewer {
actor {
name
}
}
}

fragment UserFragment on User {
username
}

レスポンスの例

{
"data": {
"user": {
"id": "4",
"name": "Mark Zuckerberg",
"username": "zuck"
},
"viewer": {
"actor": {
"name": "Your Name"
}
}
}
}

クエリのレンダリング

Relayでクエリをレンダリングするには、usePreloadedQueryフックを使用できます。usePreloadedQueryは、クエリ定義とクエリ参照を受け取り、そのクエリと参照に対応するデータを返します。

import type {HomeTabQuery} from 'HomeTabQuery.graphql';
import type {PreloadedQuery} from 'react-relay';

const React = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');

type Props = {
queryRef: PreloadedQuery<HomeTabQuery>,
};

function HomeTab(props: Props) {
const data = usePreloadedQuery(
graphql`
query HomeTabQuery($id: ID!) {
user(id: $id) {
name
}
}
`,
props.queryRef,
);

return (
<h1>{data.user?.name}</h1>
);
}

ここで何が起こっているのか見てみましょう

  • usePreloadedQueryは、graphqlクエリとPreloadedQuery参照を受け取り、そのクエリでフェッチされたデータを返します。
    • PreloadedQuery(この場合はqueryRef)は、フェッチ中(またはフェッチされた)クエリのインスタンスを記述および参照するオブジェクトです。
      • 次のセクションで実際にクエリをフェッチする方法を説明し、Suspenseによるローディング状態のセクションで、クエリをレンダリングしようとしたときにクエリが進行中の場合にローディング状態を表示する方法を説明します。
  • フラグメントと同様に、コンポーネントはクエリデータの更新を自動的にサブスクライブします。このクエリのデータがアプリ内のどこかで更新されると、コンポーネントは最新の更新されたデータで自動的に再レンダリングされます。
  • usePreloadedQueryは、クエリのFlow型に対応するFlow型パラメータも受け取ります。この場合はHomeTabQueryです。
    • Relayコンパイラは、宣言されたクエリのFlow型を自動的に生成します。これらの型は、次の名前形式の生成ファイルからインポートできます: <query_name>.graphql.js
    • dataは、明示的な注釈を必要とせずにすでに適切にFlow型指定されており、GraphQLスキーマからの型に基づいていることに注意してください。たとえば、上記のdataの型は次のようになります: { user: ?{ name: ?string } }
  • クエリをレンダリングする前に、アプリのルートでRelay環境プロバイダを使用してRelay環境を提供していることを確認してください。

レンダリング用のクエリのフェッチ

クエリのレンダリングとは別に、サーバーからフェッチする必要もあります。通常、アプリのルートのどこかでクエリをフェッチし、画面のレンダリングに必要なすべてのデータを蓄積するクエリを1つまたはいくつかのみ持つようにします。理想的には、アプリのレンダリングを開始する前のできるだけ早い段階でフェッチします。

後でレンダリングするためにクエリをフェッチするには、useQueryLoaderフックを使用できます。

import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql';
import type {PreloadedQuery} from 'react-relay';

const HomeTabQuery = require('HomeTabQuery.graphql')
const {useQueryLoader} = require('react-relay');


type Props = {
initialQueryRef: PreloadedQuery<HomeTabQueryType>,
};

function AppTabs(props) {
const [
homeTabQueryRef,
loadHomeTabQuery,
] = useQueryLoader(
HomeTabQuery,
props.initialQueryRef, /* e.g. provided by router */
);

const onSelectHomeTab = () => {
// Start loading query for HomeTab immediately in the event handler
// that triggers navigation to that tab, *before* we even start
// rendering the target tab.
// Calling this function will update the value of homeTabQueryRef.
loadHomeTabQuery({id: '4'});

// ...
}

// ...

return (
screen === 'HomeTab' && homeTabQueryRef != null ?
// Pass to component that uses usePreloadedQuery
<HomeTab queryRef={homeTabQueryRef} /> :
// ...
);
}

上記の例はやや技巧的ですが、何が起こっているのかを抽出してみましょう

  • AppTabsコンポーネント内でuseQueryLoaderを呼び出しています。
    • この場合、クエリはHomeTabQuery(前の例で宣言したクエリ)です。これは、自動生成されたファイル'HomeTabQuery.graphql'をrequireすることで取得できます。
    • オプションの初期PreloadedQueryを受け取ります。これは、状態に保存され、useQueryLoaderによって返されるhomeTabQueryRefの初期値として使用されます。
    • また、クエリのFlow型に対応するFlow型パラメータも追加で受け取ります。この場合、HomeTabQueryTypeです。これも自動生成されたファイル'HomeTabQuery.graphql'から取得できます。
  • useQueryLoaderを呼び出すと、2つのものが取得できます。
    • homeTabQueryRef: ?PreloadedQuery。これは、フェッチ中(またはフェッチされた)クエリのインスタンスを記述および参照するオブジェクトです。この値は、クエリをフェッチしていない場合、つまりloadHomeTabQueryを呼び出していない場合はnullになります。
    • loadHomeTabQuery: サーバーからこのクエリのデータをフェッチする(まだキャッシュされていない場合)関数。また、クエリが期待する変数(この場合は{id: '4'})を含むオブジェクトが与えられます(Relayがキャッシュデータを使用する方法の詳細については、レンダリング用のキャッシュデータの再利用のセクションで説明します)。この関数を呼び出すと、homeTabQueryRefの値もPreloadedQueryのインスタンスに更新されます。
      • この関数に渡すvariablesは、Flowによってチェックされ、GraphQLクエリが期待する値と一致する値を渡していることが保証されます。
      • また、HomeTabがレンダリングされる原因となるイベントハンドラー内でこの関数を呼び出していることに注意してください。これにより、新しいタブのレンダリングを開始する前でも、できるだけ早く画面のデータのフェッチを開始できます。
        • 実際、loadQueryは、Reactのレンダリングフェーズ中に呼び出されるとエラーをスローします!
  • useQueryLoaderは、コンポーネントがアンマウントされると、ロードされたすべてのクエリを自動的に破棄することに注意してください。クエリを破棄するということは、Relayがキャッシュ内のクエリの特定のインスタンスのデータを保持しなくなることを意味します(クエリデータのライフサイクルについては、レンダリング用のキャッシュデータの再利用のセクションで説明します)。また、破棄が発生したときにクエリのリクエストがまだ進行中の場合は、キャンセルされます。
  • AppTabsコンポーネントは、前の例のHomeTabコンポーネントをレンダリングし、対応するクエリ参照を渡します。この親コンポーネントは、そのクエリのデータのライフサイクルを所有していることに注意してください。つまり、アンマウントすると、上記のようにそのクエリを破棄します。
  • 最後に、useQueryLoaderを使用する前に、アプリのルートでRelay環境プロバイダを使用してRelay環境を提供していることを確認してください。

場合によっては、親コンポーネントのコンテキスト外でフェッチを開始したいことがあります。たとえば、アプリケーションの初期ロードに必要なデータをフェッチする場合などです。このような場合は、useQueryLoaderを使用せずに、loadQuery APIを直接使用できます。

import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql';

const HomeTabQuery = require('HomeTabQuery.graphql')
const {loadQuery} = require('react-relay');


const environment = createEnvironment(...);

// At some point during app initialization
const initialQueryRef = loadQuery<HomeTabQueryType>(
environment,
HomeTabQuery,
{id: '4'},
);

// ...

// E.g. passing the initialQueryRef to the root component
render(<AppTabs initialQueryRef={initialQueryRef} initialTab={...} />)
  • この例では、loadQuery関数を直接呼び出して、後でusePreloadedQueryを使用するコンポーネントに渡すことができるPreloadedQueryインスタンスを取得しています。
  • この場合、ルートのAppTabsコンポーネントがクエリ参照のライフサイクルを管理し、適切なタイミングで(もしあれば)破棄することが期待されます。
  • この例では、「アプリの初期化」の詳細は曖昧なままにしていますが、これはアプリケーションごとに異なるためです。ここで重要なことは、ルートコンポーネントのレンダリングを開始する前にクエリ参照を取得する必要があるということです。実際、loadQueryは、Reactのレンダリングフェーズ中に呼び出されるとエラーをスローします!

フェッチしながらレンダリング

上記の例は、データのフェッチをレンダリングから分離し、フェッチをできるだけ早く開始する(コンポーネントがレンダリングされてフェッチを開始するのを待つのではなく)方法を示しており、ユーザーにはるかに早くコンテンツを表示できるようにします。また、ウォーターフォール往復を防止し、いつフェッチが発生するかをより詳細に制御および予測できるようにします。一方、レンダリング中にフェッチすると、いつフェッチが発生するか(または発生するべきか)を判断するのが難しくなります。これは、「フェッチしながらレンダリング」パターンとReact Suspenseとうまく適合します。

これはRelayでデータをフェッチするための推奨パターンであり、アプリケーションの初期ロード、その後のナビゲーション中、または最初非表示で、インタラクション(メニュー、ポップオーバー、ダイアログなど)で後で表示され、追加データのフェッチも必要なUI要素を使用する場合など、いくつかの状況で適用されます。

レンダリング中にクエリを遅延フェッチする

クエリをフェッチするもう1つの代替方法は、コンポーネントがレンダリングされたときにクエリを遅延フェッチすることです。ただし、前述したように、推奨されるパターンは、レンダリング前にクエリのフェッチを開始することです。注意せずに遅延フェッチを使用すると、ネストされたまたはウォーターフォール往復が発生し、パフォーマンスが低下する可能性があります。

クエリを遅延フェッチするには、useLazyLoadQueryフックを使用できます。

const React = require('React');
const {graphql, useLazyLoadQuery} = require('react-relay');

function App() {
const data = useLazyLoadQuery(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
}
}
`,
{id: '4'},
);

return (
<h1>{data.user?.name}</h1>
);
}

ここで何が起こっているのか見てみましょう

  • useLazyLoadQueryは、graphqlクエリとそのクエリのいくつかの変数を受け取り、そのクエリでフェッチされたデータを返します。変数は、GraphQLクエリ内で参照される変数の値を含むオブジェクトです。
  • フラグメントと同様に、コンポーネントはクエリデータの更新を自動的にサブスクライブします。このクエリのデータがアプリ内のどこかで更新されると、コンポーネントは最新の更新されたデータで自動的に再レンダリングされます。
  • useLazyLoadQueryは、クエリのFlow型に対応するFlow型パラメータも追加で受け取ります。この場合はAppQueryです。
    • Relayは、宣言されたクエリのFlow型を自動的に生成します。これらは、useLazyLoadQueryでインポートして使用できます。これらの型は、次の名前形式の生成ファイルで使用できます: <query_name>.graphql.js
    • variablesは、Flowによってチェックされ、GraphQLクエリが期待する値と一致する値を渡していることが保証されます。
    • データは明示的なアノテーションを必要とせず、GraphQLスキーマの型に基づいて、すでに適切にFlow型付けされていることに注意してください。例えば、上記のdataの型は{ user: ?{ name: ?string } }のようになります。
  • デフォルトでは、コンポーネントがレンダリングされると、Relayはこのクエリのデータを(まだキャッシュされていない場合)フェッチし、useLazyLoadQuery呼び出しの結果として返します。ローディング状態の表示方法についてはSuspenseを使ったローディング状態のセクションで、Relayがキャッシュされたデータをどのように使用するかについてはレンダリングのためのキャッシュされたデータの再利用のセクションで詳しく説明します。
  • コンポーネントを再レンダリングし、元々使用したクエリ変数と異なるものを渡すと、新しい変数でクエリが再度フェッチされ、異なるデータで再レンダリングされる可能性があることに注意してください。
  • 最後に、クエリをレンダリングする前に、アプリのルートでRelay Environment Providerを使用してRelay環境を提供していることを確認してください。

このページは役に立ちましたか?

サイトをさらに良くするためにご協力ください 簡単な質問にいくつかお答えください.