フラグメント
Relay で React コンポーネントのデータ依存関係を宣言するための主要な構成要素は、GraphQL フラグメントです。フラグメントは GraphQL における再利用可能な単位であり、スキーマで公開されている GraphQL タイプからクエリするデータセットを表します。
実際には、GraphQL タイプのフィールドの選択です。
fragment UserFragment on User {
name
age
profile_picture(scale: 2) {
uri
}
}
JavaScript コード内でフラグメントを宣言するには、graphql
タグを使用する必要があります。
const {graphql} = require('react-relay');
const userFragment = graphql`
fragment UserFragment_user on User {
name
age
profile_picture(scale: 2) {
uri
}
}
`;
フラグメントのレンダリング
フラグメントのデータを*レンダリング*するには、useFragment
Hook を使用できます。
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
const React = require('React');
const {graphql, useFragment} = require('react-relay');
type Props = {
user: UserComponent_user$key,
};
function UserComponent(props: Props) {
const data = useFragment(
graphql`
fragment UserComponent_user on User {
name
profile_picture(scale: 2) {
uri
}
}
`,
props.user,
);
return (
<>
<h1>{data.name}</h1>
<div>
<img src={data.profile_picture?.uri} />
</div>
</>
);
}
module.exports = UserComponent;
ここで何が起こっているのかを詳しく見てみましょう。
useFragment
は、フラグメント定義と*フラグメント参照*を受け取り、そのフラグメントと参照に対応するdata
を返します。- これは、クエリ定義とクエリ参照を受け取る
usePreloadedQuery
と似ています。
- これは、クエリ定義とクエリ参照を受け取る
- *フラグメント参照*は、Relay がフラグメント定義で宣言されたデータを読み取るために使用するオブジェクトです。ご覧のとおり、
UserComponent_user
フラグメント自体はUser
タイプのフィールドを宣言するだけですが、どの特定のユーザーからそれらのフィールドを読み取る必要があるかを知る必要があります。これがフラグメント参照に対応します。言い換えれば、フラグメント参照は、データを読み取りたいタイプの*特定のインスタンスへのポインタ*のようなものです。 - *コンポーネントはフラグメントデータの更新を自動的に購読することに注意してください*。この特定の
User
のデータがアプリのどこかで更新された場合(たとえば、新しいデータのフェッチや既存データの変更など)、コンポーネントは最新の更新されたデータで自動的に再レンダリングされます。 - Relay は、コンパイラの実行時に宣言されたフラグメントの Flow タイプを自動的に生成するため、これらのタイプを使用してコンポーネントの
props
のタイプを宣言できます。- 生成された Flow タイプには、
$key
サフィックスが付いたタイプであるフラグメント参照のタイプ(<fragment_name>$key
)と、$data
サフィックスが付いたタイプであるデータの形状のタイプ(<fragment_name>$data
)が含まれています。これらのタイプは、次の名前で生成されたファイルからインポートできます:<fragment_name>.graphql.js
。 useFragment
を使用する場合、フラグメント参照プロップのタイプが正しく宣言されていることを強制するために、lint ルールを使用しています。適切に型付けされたフラグメント参照を入力として使用することにより、返されるdata
のタイプは、明示的なアノテーションを必要とせずに自動的に Flow 型付けされます。- この例では、
user
プロップをuseFragment
に必要なフラグメント参照として型付けしています。これは、UserComponent_user.graphql
からインポートされたUserComponent_user$key
に対応します。つまり、上記のdata
のタイプは{ name: ?string, profile_picture: ?{ uri: ?string } }
になります。
- 生成された Flow タイプには、
- フラグメント名はグローバルに一意である必要があります。これを簡単に実現するために、モジュール名と識別子に基づいて、次の規則を使用してフラグメントに名前を付けます。
<module_name>_<property_name>
。 これにより、どのフラグメントがどのモジュールで定義されているかを簡単に識別でき、同じモジュールで複数のフラグメントが定義されている場合の名前の衝突を回避できます。
同じコンポーネント内で複数のフラグメントからのデータをレンダリングする必要がある場合は、useFragment
を複数回使用できます。
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
import type {UserComponent_viewer$key} from 'UserComponent_viewer.graphql';
const React = require('React');
const {graphql, useFragment} = require('react-relay');
type Props = {
user: UserComponent_user$key,
viewer: UserComponent_viewer$key,
};
function UserComponent(props: Props) {
const userData = useFragment(
graphql`
fragment UserComponent_user on User {
name
profile_picture(scale: 2) {
uri
}
}
`,
props.user,
);
const viewerData = useFragment(
graphql`
fragment UserComponent_viewer on Viewer {
actor {
name
}
}
`,
props.viewer,
);
return (
<>
<h1>{userData.name}</h1>
<div>
<img src={userData.profile_picture?.uri} />
Acting as: {viewerData.actor?.name ?? 'Unknown'}
</div>
</>
);
}
module.exports = UserComponent;
フラグメントの合成
GraphQL では、フラグメントは再利用可能な単位です。つまり、*他の*フラグメントを含めることができ、その結果、フラグメントを他のフラグメントまたはクエリに含めることができます。
fragment UserFragment on User {
name
age
profile_picture(scale: 2) {
uri
}
...AnotherUserFragment
}
fragment AnotherUserFragment on User {
username
...FooUserFragment
}
Relay を使用すると、コンポーネントの合成とフラグメントの合成の両方を使用して、同様の方法でフラグメントコンポーネントを合成できます。 各 React コンポーネントは、直接の子のデータ依存関係をフェッチする役割を担います。これは、子コンポーネントを正しくレンダリングするために、子のプロップについて知る必要があるのと同じです。 このパターンは、開発者がコンポーネントについてローカルに推論できることを意味します(必要なデータ、レンダリングするコンポーネントなど)。しかし、Relay は UI ツリー全体のデータ依存関係のグローバルビューを取得できます。
/**
* UsernameSection.react.js
*
* Child Fragment Component
*/
import type {UsernameSection_user$key} from 'UsernameSection_user.graphql';
const React = require('React');
const {graphql, useFragment} = require('react-relay');
type Props = {
user: UsernameSection_user$key,
};
function UsernameSection(props: Props) {
const data = useFragment(
graphql`
fragment UsernameSection_user on User {
username
}
`,
props.user,
);
return <div>{data.username ?? 'Unknown'}</div>;
}
module.exports = UsernameSection;
/**
* UserComponent.react.js
*
* Parent Fragment Component
*/
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
const React = require('React');
const {graphql, useFragment} = require('react-relay');
const UsernameSection = require('./UsernameSection.react');
type Props = {
user: UserComponent_user$key,
};
function UserComponent(props: Props) {
const user = useFragment(
graphql`
fragment UserComponent_user on User {
name
age
profile_picture(scale: 2) {
uri
}
# Include child fragment:
...UsernameSection_user
}
`,
props.user,
);
return (
<>
<h1>{user.name}</h1>
<div>
<img src={user.profile_picture?.uri} />
{user.age}
{/* Render child component, passing the _fragment reference_: */}
<UsernameSection user={user} />
</div>
</>
);
}
module.exports = UserComponent;
ここで注意すべき点がいくつかあります。
UserComponent
は、UsernameSection
をレンダリングするだけでなく、独自のgraphql
フラグメント宣言内にUsernameSection
によって宣言されたフラグメントを含みます。UsernameSection
は、user
プロップとして*フラグメント参照*を予期します。前述のとおり、フラグメント参照は、Relay がフラグメント定義で宣言されたデータを読み取るために使用するオブジェクトです。ご覧のとおり、子UsernameSection_user
フラグメント自体はUser
タイプのフィールドを宣言するだけですが、どの特定のユーザーからそれらのフィールドを読み取る必要があるかを知る必要があります。これがフラグメント参照に対応します。言い換えれば、フラグメント参照は、データを読み取りたいタイプの*特定のインスタンスへのポインタ*のようなものです。- この場合、
UsernameSection
に渡されるuser
、つまりフラグメント参照は、*実際には子UsernameSection
コンポーネントによって宣言されたデータを含んでいない*ことに注意してください。代わりに、UsernameSection
はフラグメント参照を使用して、useFragment
を使用して内部で宣言されたデータを読み取ります。 - これは、親コンポーネントが子コンポーネントによって選択されたデータを受信しないことを意味します(親が明示的に同じフィールドを選択しない限り)。同様に、子コンポーネントは、親によって選択されたデータを受信しません(ここでも、子が同じフィールドを選択しない限り)。
- これにより、個別のコンポーネントが*偶発的に*互いに暗黙的な依存関係を持つことを防ぎます。そうでない場合、コンポーネントを変更すると他のコンポーネントが壊れる可能性があります!
- これにより、他のコンポーネントに影響を与えることを心配することなく、コンポーネントについてローカルに推論し、変更することができます。
- これは、 *データマスキング*と呼ばれます。
- 子(つまり
UsernameSection
)が予期する*フラグメント参照*は、子フラグメントを*含む*親フラグメントを読み取った結果です。この特定の例では、...UsernameSection_user
を含むフラグメントを読み取った結果が、UsernameSection
が予期するフラグメント参照になります。言い換えれば、useFragment
を介してフラグメントを読み取った結果として得られたデータは、そのフラグメントに含まれる子フラグメントのフラグメント参照としても機能します.
フラグメントをクエリに合成する
Relay のフラグメントを使用すると、コンポーネントのデータ依存関係を宣言できますが、* **単独でフェッチすることはできません** *。代わりに、直接または推移的にクエリに含める必要があります。これは、*すべてのフラグメントがレンダリング時にクエリに属している必要がある*ことを意味します。言い換えれば、一部のクエリの「ルート」の下にある必要があります。単一のフラグメントは依然として複数のクエリに含めることができますが、フラグメントコンポーネントの特定の*インスタンス*をレンダリングする場合、特定のクエリリクエストの一部として含まれている必要があります。
フラグメントを含むクエリをフェッチしてレンダリングするには、フラグメントの合成セクションに示すように、フラグメントを合成するのと同じ方法で合成できます。
/**
* UserComponent.react.js
*
* Fragment Component
*/
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
const React = require('React');
const {graphql, useFragment} = require('react-relay');
type Props = {
user: UserComponent_user$key,
};
function UserComponent(props: Props) {
const data = useFragment(
graphql`...`,
props.user,
);
return (...);
}
module.exports = UserComponent;
/**
* App.react.js
*
* Query Component
*/
import type {AppQuery} from 'AppQuery.graphql';
import type {PreloadedQuery} from 'react-relay';
const React = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');
const UserComponent = require('./UserComponent.react');
type Props = {
appQueryRef: PreloadedQuery<AppQuery>,
}
function App({appQueryRef}) {
const data = usePreloadedQuery(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
# Include child fragment:
...UserComponent_user
}
}
`,
appQueryRef,
);
return (
<>
<h1>{data.user?.name}</h1>
{/* Render child component, passing the fragment reference: */}
<UserComponent user={data.user} />
</>
);
}
次の点に注意してください。
UserComponent
が予期する*フラグメント参照*は、そのフラグメントを含む親クエリを読み取った結果です。この例では、...UsernameSection_user
を含むクエリです。言い換えれば、usePreloadedQuery
の結果として得られたdata
は、そのクエリに含まれる子フラグメントのフラグメント参照としても機能します。- 前述のとおり、*すべてのフラグメントはレンダリング時にクエリに属している必要があります*。これは、すべてのフラグメントコンポーネントがクエリの*子孫*である必要があることを意味します。これにより、
usePreloadedQuery
を使用してルートクエリを読み取った結果から始めて、useFragment
のフラグメント参照を常に提供できるようになります。
このページは役に立ちましたか?
いくつかの簡単な質問に答えることで、サイトの改善にご協力ください。 いくつかの簡単な質問に答える.