@alias ディレクティブ
@alias
ディレクティブを使用すると、名前付きフラグメントスプレッドまたはインラインフラグメントのいずれかのスプレッドフラグメントを、選択範囲内の名前付きフィールドとして公開できます。 これにより、フラグメントの型が親の選択と一致しない場合に、Relay が追加の型安全性を提供できます。
このドキュメントでは、@alias
ディレクティブが導入された理由と、Relay アプリケーションの型安全性を向上させるためにどのように使用できるかについて説明します。 API については、API リファレンスを参照してください。
@alias
が役立つ例を見てみましょう
抽象型
Viewer に関する情報をレンダリングするコンポーネントがあるとします
function MyViewer({viewerKey}) {
const {name} = useFragment(graphql`
fragment MyViewer on Viewer {
name @required(action: THROW)
}`, viewerKey);
return `My name is ${name}. That's ${name.length} letters long!`;
}
そのコンポーネントを Node(Viewer が実装している)のフラグメントを持つコンポーネントで使用するには、次のように記述できます
function MyNode({nodeKey}) {
const node = useFragment(graphql`
fragment MyFragment on Node {
...MyViewer
}`, nodeKey);
return <MyViewer viewerKey={node} />
}
問題点がわかりますか? <MyViewer />
に渡しているノードが実際に Viewer <MyViewer />
であるかどうかは実際にはわかりません。 <MyNode />
が Comment(これも Node を実装している)をレンダリングしようとすると、フィールド名が Comment に存在しないため、<MyViewer />
でランタイムエラーが発生します。
TypeError: Cannot read properties of undefined (reading 'length')
この潜在的な問題について知らせる型を取得できないだけでなく、ランタイムでも、Viewer が抽象型であるため、ノードが Viewer を実装しているかどうかを確認する方法がありません!
エイリアス付きフラグメント
エイリアス付きフラグメントはこの問題を解決できます。 それらを使用して <MyNode />
がどのように見えるかを示します
function MyNode({nodeKey}) {
const node = useFragment(graphql`
fragment MyFragment on Node {
...MyViewer @alias(as: "my_viewer")
}`, nodeKey);
// Relay returns the fragment key as its own nullable property
if(node.my_viewer == null) {
return null;
}
// Because `my_viewer` is typed as nullable, Flow/TypeScript will
// show an error if you try to use the `my_viewer` without first
// performing a null check.
// VVVVVVVVVVVVVV
return <MyViewer viewerKey={node.my_viewer} />
}
このアプローチでは、Relay がフラグメントキーを独自の null 許容プロパティとして公開していることがわかります。これにより、ノードが実際に Viewer を実装していることを確認でき、Flow がコンポーネントが可能性を処理することを強制することさえできます!
@skip と @include
フラグメントで @skip
および @include
ディレクティブを使用する場合にも同様の問題が発生する可能性があります。 スプレッドフラグメントを安全に使用するには、それがフェッチされたかどうかを確認する必要があります。 従来、これはフラグメントがスキップされたか含まれたかを判断するために使用されたクエリ変数へのアクセスを取得する必要がありました。
@alias
を使用すると、フラグメントにエイリアスを割り当て、エイリアスが null かどうかを確認するだけで、フラグメントがフェッチされたかどうかを確認できるようになりました
function MyUser({userKey}) {
const user = useFragment(graphql`
fragment MyFragment on User {
...ConditionalData @skip(if: $someVar) @alias
}`, userKey);
if(user.ConditionalData == null) {
return "No data fetched";
}
return <ConditionalData userKey={user.ConditionalData} />
}
強制的な安全性
@alias
なしの場合、Relay でフラグメントが安全でない可能性のある 2 つの異なる方法について概説しました。 これらの安全でないエッジケースに起因するランタイムの問題を防ぐために、Relay はまもなく、条件付きでフェッチされたすべてのフラグメントにエイリアスを付けることを要求します。
プロジェクトでこの検証を今すぐ試すには、プロジェクトの実験的な enforce_fragment_alias_where_ambiguous
コンパイラ機能フラグを有効にすることができます。 この強制の段階的な採用を可能にするために、Relay は、これらの強制エラーを抑制するディレクティブ @dangerously_unaliased_fixme
を公開しています。 これにより、既存のすべての問題を最初に移行することなく、すべての新しいスプレッドに対して強制を有効にすることができます.
Relay VSCode 拡張機能は、安全でないフラグメントに @alias
または @dangerously_unaliased_fixme
を追加するためのクイックフィックスを提供します。
@required と併用する
@alias
は @required(action: NONE)
と一緒に使用して、必須フィールドをグループ化できます。 次の例では、name
と email
を requiredFields
としてグループ化しています。 いずれかが null の場合、その null は user.requiredFields
フィールドにバブルアップされ、null になります。 これにより、id
フィールドに影響を与えることなく、単一のチェックを実行できます。
function MyUser({userKey}) {
const user = useFragment(graphql`
fragment MyFragment on User {
id
... @alias(as: "requiredFields") {
name @required(action: NONE)
email @required(action: NONE)
}
}`, userKey);
if(user.requiredFields == null) {
return `Missing required fields for user ${user.id}`;
}
return `Hello ${user.requiredFields.name} (${user.requiredFields.email}).!`;
}
@alias
を持つフラグメントスプレッドで @required
を使用することは現在サポートされていませんが、将来サポートを追加する可能性があります。
内部実装
Relay に精通している方、または知りたい方のために、この機能の実装方法について簡単に説明します
内部的には、@alias
は Relay(コンパイラとランタイム)内で完全に実装されています。 サーバーのサポートは必要ありません。 Relay コンパイラは @alias
ディレクティブを解釈し、フラグメントキーまたはインラインフラグメントデータが親オブジェクトに直接ではなく、新しいフィールドに添付されることを示す型を生成します。 Relay ランタイムアーティファクトでは、フラグメントノードを、エイリアスの名前とフラグメントの型に関する追加情報を示す新しいノードでラップします。
Relay コンパイラは、フラグメントが一致したかどうかを判断できるように、スプレッドに追加のフィールドも挿入します
fragment Foo on Node {
... on Viewer {
isViewer: __typename # <-- Relay inserts this
name
}
}
Relay はレスポンスで isViewer
フィールドの存在を確認して、フラグメントが一致したかどうかを確認できるようになりました。
Relay がランタイムアーティファクトを使用してストアからフラグメントの内容を読み取ると、この情報を使用して、フラグメントキーを親オブジェクトに直接添付するのではなく、この新しいフィールドに添付します。
関連情報
@alias
は Relay 固有の機能ですが、GraphQL RFC フラグメントモジュール性で概説されているフラグメントモジュール性からインスピレーションを得ています。