接続の更新
通常、接続をレンダリングする際には、ユーザーのアクションに応じて接続にアイテムを追加または削除できるようにする必要があります。
データ更新セクションで説明したように、Relayは正規化されたGraphQLデータのローカルインメモリストアを保持しており、レコードはIDによって保存されます。Relayでミューテーション、サブスクリプション、またはローカルデータ更新を作成する際には、updater
関数を提供する必要があります。この関数内で、レコードにアクセスして読み取ったり、レコードに書き込みを行ったり、更新したりできます。レコードが更新されると、更新されたデータの影響を受けるコンポーネントはすべて通知され、再レンダリングされます。
接続レコード
Relayでは、@connection
ディレクティブでマークされた接続フィールドは、ストア内の特別なレコードとして保存され、接続に対してこれまでにフェッチされたすべてのアイテムを保持および累積します。接続にアイテムを追加または削除するには、@connection
を宣言するときに提供された接続key
を使用して接続レコードにアクセスする必要があります。具体的には、これにより、updater
関数内でConnectionHandler
APIを使用して接続にアクセスできます。
たとえば、@connection
を宣言する次のフラグメントが与えられた場合、updater
関数内の接続レコードにいくつかの異なる方法でアクセスできます。
const {graphql} = require('react-relay');
const storyFragment = graphql`
fragment StoryComponent_story on Story {
comments @connection(key: "StoryComponent_story_comments_connection") {
nodes {
body {
text
}
}
}
}
`;
__id
を使用した接続へのアクセス
接続の__id
フィールドをクエリし、その__id
を使用してストア内のレコードにアクセスできます。
const fragmentData = useFragment(
graphql`
fragment StoryComponent_story on Story {
comments @connection(key: "StoryComponent_story_comments_connection") {
# Query for the __id field
__id
# ...
}
}
`,
props.story,
);
// Get the connection record id
const connectionID = fragmentData?.comments?.__id;
次に、それを使用してストア内のレコードにアクセスします。
function updater(store: RecordSourceSelectorProxy) {
// connectionID is passed as input to the mutation/subscription
const connection = store.get(connectionID);
// ...
}
__id
フィールドは、GraphQL APIが公開する必要があるものではありません。代わりに、Relayが接続レコードを識別するために自動的に追加する識別子です。
ConnectionHandler.getConnectionID
を使用した接続へのアクセス
接続を保持する親レコードのIDにアクセスできる場合は、ConnectionHandler.getConnectionID
APIを使用して接続レコードにアクセスできます。
const {ConnectionHandler} = require('relay-runtime');
function updater(store: RecordSourceSelectorProxy) {
// Get the connection ID
const connectionID = ConnectionHandler.getConnectionID(
storyID, // passed as input to the mutation/subscription
'StoryComponent_story_comments_connection',
);
// Get the connection record
const connectionRecord = store.get(connectionID);
// ...
}
ConnectionHandler.getConnection
を使用した接続へのアクセス
接続を保持する親レコードにアクセスできる場合は、ConnectionHandler.getConnection
APIを使用して、親経由で接続レコードにアクセスできます。
const {ConnectionHandler} = require('relay-runtime');
function updater(store: RecordSourceSelectorProxy) {
// Get parent story record
// storyID is passed as input to the mutation/subscription
const storyRecord = store.get(storyID);
// Get the connection record from the parent
const connectionRecord = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
);
// ...
}
エッジの追加
接続にエッジを追加するには、いくつかの代替手段があります。
宣言的ディレクティブの使用
通常、ミューテーションまたはサブスクリプションのペイロードは、サーバーに追加された新しいエッジを、単一のエッジまたはエッジのリストを持つフィールドとして公開します。ミューテーションまたはサブスクリプションが、レスポンスでクエリできるエッジまたはエッジフィールドを公開する場合は、@appendEdge
および@prependEdge
宣言ミューテーションディレクティブをそのフィールドで使用して、新しく作成されたエッジを指定された接続に追加できます(これらのディレクティブはクエリでも機能することに注意してください)。
または、ミューテーションまたはサブスクリプションのペイロードが、サーバーに追加された新しいノードを、単一のノードまたはノードのリストを持つフィールドとして公開する場合があります。ミューテーションまたはサブスクリプションが、レスポンスでクエリできるノードまたはノードフィールドを公開する場合は、@appendNode
および@prependNode
宣言ミューテーションディレクティブをそのフィールドで使用して、新しく作成されたノードをエッジ内にラップして、指定された接続に追加できます(これらのディレクティブはクエリでも機能することに注意してください)。
これらのディレクティブは、接続IDの配列を含むGraphQL変数である必要があるconnections
パラメーターを受け入れます。接続IDは、接続の__id
フィールドを使用するか、ConnectionHandler.getConnectionID
APIを使用して取得できます。
@appendEdge
/ @prependEdge
これらのディレクティブは、単一のエッジまたはエッジのリストを持つフィールドで機能します。@prependEdge
は、選択されたエッジをconnections
配列で定義された各接続の先頭に追加し、@appendEdge
は、選択されたエッジを配列内の各接続の末尾に追加します。
引数
connections
:接続IDの配列。接続IDは、接続の__id
フィールドを使用するか、ConnectionHandler.getConnectionID
APIを使用して取得できます。
例
// Get the connection ID using the `__id` field
const connectionID = fragmentData?.comments?.__id;
// Or get it using `ConnectionHandler.getConnectionID()`
const connectionID = ConnectionHandler.getConnectionID(
'<story-id>',
'StoryComponent_story_comments_connection',
);
// ...
// Mutation
commitMutation<AppendCommentMutation>(environment, {
mutation: graphql`
mutation AppendCommentMutation(
# Define a GraphQL variable for the connections array
$connections: [ID!]!
$input: CommentCreateInput
) {
commentCreate(input: $input) {
# Use @appendEdge or @prependEdge on the edge field
feedbackCommentEdge @appendEdge(connections: $connections) {
cursor
node {
id
}
}
}
}
`,
variables: {
input,
// Pass the `connections` array
connections: [connectionID],
},
});
@appendNode
/ @prependNode
これらのディレクティブは、単一のノードまたはノードのリストを持つフィールドで機能し、指定されたedgeTypeName
でエッジを作成します。@prependNode
は、選択されたノードを含むエッジを、connections
配列で定義された各接続の先頭に追加し、@appendNode
は、選択されたノードを含むエッジを、配列内の各接続の末尾に追加します。
引数
connections
:接続IDの配列。接続IDは、接続の__id
フィールドを使用するか、ConnectionHandler.getConnectionID
APIを使用して取得できます。edgeTypeName
:ノードを含むエッジのタイプ名。ConnectionHandler.createEdge
のエッジタイプ引数に対応します。
例
// Get the connection ID using the `__id` field
const connectionID = fragmentData?.comments?.__id;
// Or get it using `ConnectionHandler.getConnectionID()`
const connectionID = ConnectionHandler.getConnectionID(
'<story-id>',
'StoryComponent_story_comments_connection',
);
// ...
// Mutation
commitMutation<AppendCommentMutation>(environment, {
mutation: graphql`
mutation AppendCommentMutation(
# Define a GraphQL variable for the connections array
$connections: [ID!]!
$input: CommentCreateInput
) {
commentCreate(input: $input) {
# Use @appendNode or @prependNode on the node field
feedbackCommentNode @appendNode(connections: $connections, edgeTypeName: "CommentsEdge") {
id
}
}
}
`,
variables: {
input,
// Pass the `connections` array
connections: [connectionID],
},
});
実行順序
これらのすべてのディレクティブは、更新の実行順序に従って、ミューテーションまたはサブスクリプション内で次の順序で実行されます。
- ミューテーションが開始されると、楽観的レスポンスが処理され、楽観的アップデーター関数が実行された後、
@prependEdge
、@appendEdge
、@prependNode
、および@appendNode
ディレクティブが楽観的レスポンスに適用されます。 - ミューテーションが成功した場合、ネットワークレスポンスからのデータがストア内の既存の値とマージされ、アップデーター関数が実行された後、
@prependEdge
、@appendEdge
、@prependNode
、および@appendNode
ディレクティブがネットワークレスポンスのデータに適用されます。 - ミューテーションが失敗した場合、
@prependEdge
、@appendEdge
、@prependNode
、および@appendNode
ディレクティブの処理による更新はロールバックされます。
手動でエッジを追加する
上記で説明したディレクティブは、接続からのアイテムの手動での追加および削除の必要性を大幅に排除しますが、手動でアップデーターを記述する場合ほど多くの制御を提供せず、すべてのユースケースを満たさない可能性があります。
接続を変更するためのアップデーターを記述するには、接続レコードにアクセスできることを確認する必要があります。接続レコードを取得したら、接続に追加する新しいエッジのレコードも必要です。通常、ミューテーションまたはサブスクリプションのペイロードには、追加された新しいエッジが含まれます。そうでない場合は、新しいエッジを最初から構築することもできます。
たとえば、次のミューテーションでは、ミューテーションレスポンスで新しく作成されたエッジをクエリできます。
const {graphql} = require('react-relay');
const createCommentMutation = graphql`
mutation CreateCommentMutation($input: CommentCreateData!) {
comment_create(input: $input) {
comment_edge {
cursor
node {
body {
text
}
}
}
}
}
`;
- また、新しいエッジの
cursor
をクエリすることにも注意してください。これは厳密には必要ありませんが、そのcursor
に基づいてページネーションを実行する必要がある場合に必要になる情報です。
updater
の内部では、Relay ストア API を使用してミューテーションレスポンス内のエッジにアクセスできます。
const {ConnectionHandler} = require('relay-runtime');
function updater(store: RecordSourceSelectorProxy) {
const storyRecord = store.get(storyID);
const connectionRecord = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
);
// Get the payload returned from the server
const payload = store.getRootField('comment_create');
// Get the edge inside the payload
const serverEdge = payload.getLinkedRecord('comment_edge');
// Build edge for adding to the connection
const newEdge = ConnectionHandler.buildConnectionEdge(
store,
connectionRecord,
serverEdge,
);
// ...
}
- ミューテーションのペイロードは、ストアのルートフィールドとして利用でき、
store.getRootField
API を使用して読み取ることができます。この例では、レスポンスのルートフィールドであるcomment_create
を読み取っています。 - 接続に追加する前に、サーバーから受信したエッジから
ConnectionHandler.buildConnectionEdge
を使用して新しいエッジを構築する必要があることに注意してください。
新しいエッジをゼロから作成する必要がある場合は、ConnectionHandler.createEdge
を使用できます。
const {ConnectionHandler} = require('relay-runtime');
function updater(store: RecordSourceSelectorProxy) {
const storyRecord = store.get(storyID);
const connectionRecord = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
);
// Create a new local Comment record
const id = `client:new_comment:${randomID()}`;
const newCommentRecord = store.create(id, 'Comment');
// Create new edge
const newEdge = ConnectionHandler.createEdge(
store,
connectionRecord,
newCommentRecord,
'CommentEdge', /* GraphQl Type for edge */
);
// ...
}
新しいエッジレコードを取得したら、ConnectionHandler.insertEdgeAfter
または ConnectionHandler.insertEdgeBefore
を使用して接続に追加できます。
const {ConnectionHandler} = require('relay-runtime');
function updater(store: RecordSourceSelectorProxy) {
const storyRecord = store.get(storyID);
const connectionRecord = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
);
const newEdge = (...);
// Add edge to the end of the connection
ConnectionHandler.insertEdgeAfter(
connectionRecord,
newEdge,
);
// Add edge to the beginning of the connection
ConnectionHandler.insertEdgeBefore(
connectionRecord,
newEdge,
);
}
- これらの API は接続をその場で変更することに注意してください。
完全な Relay ストア API をご確認ください。
エッジの削除
宣言的な削除ディレクティブの使用
エッジを追加するためのディレクティブと同様に、@deleteEdge
ディレクティブを使用して接続からエッジを削除できます。ミューテーションまたはサブスクリプションが、レスポンスでクエリできる削除されたノードの ID を持つフィールドを公開している場合、そのフィールドで @deleteEdge
ディレクティブを使用して、接続から対応するエッジを削除できます(このディレクティブはクエリでも機能することに注意してください)。
@deleteEdge
ID
または [ID]
を返す GraphQL フィールドで機能します。connections
配列で定義された各接続から、id
に一致するノードを持つエッジを削除します。
引数
connections
:接続IDの配列。接続IDは、接続の__id
フィールドを使用するか、ConnectionHandler.getConnectionID
APIを使用して取得できます。
例
// Get the connection ID using the `__id` field
const connectionID = fragmentData?.comments?.__id;
// Or get it using `ConnectionHandler.getConnectionID()`
const connectionID = ConnectionHandler.getConnectionID(
'<story-id>',
'StoryComponent_story_comments_connection',
);
// ...
// Mutation
commitMutation<DeleteCommentsMutation>(environment, {
mutation: graphql`
mutation DeleteCommentsMutation(
# Define a GraphQL variable for the connections array
$connections: [ID!]!
$input: CommentsDeleteInput
) {
commentsDelete(input: $input) {
deletedCommentIds @deleteEdge(connections: $connections)
}
}
`,
variables: {
input,
// Pass the `connections` array
connections: [connectionID],
},
});
手動でのエッジの削除
ConnectionHandler
は、ConnectionHandler.deleteNode
を介して接続からエッジを削除するための同様の API を提供します。
const {ConnectionHandler} = require('RelayModern');
function updater(store: RecordSourceSelectorProxy) {
const storyRecord = store.get(storyID);
const connectionRecord = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
);
// Remove edge from the connection, given the ID of the node
ConnectionHandler.deleteNode(
connectionRecord,
commentIDToDelete,
);
}
- この場合、
ConnectionHandler.deleteNode
は、node
ID を指定すると、エッジを削除します。つまり、接続内のどのエッジに提供された ID を持つノードが含まれているかを探し、そのエッジを削除します。 - この API は接続をその場で変更することに注意してください。
注意:ここで説明した操作を実行して接続を変更する場合、影響を受ける接続をレンダリングしているすべてのフラグメントまたはクエリコンポーネントは通知され、最新バージョンの接続で再レンダリングされます。
フィルターを使用した接続の識別
前の例では、接続はフィルターとして引数を取っていませんでした。引数をフィルターとして受け取る接続を宣言した場合、フィルターに使用される値は接続識別子の一部になります。言い換えれば、接続フィルターとして渡される各値は、Relay ストアで接続を識別するために使用されます。
これは、ページネーション引数、つまり first
、last
、before
、および after
を除外することに注意してください。
たとえば、comments
フィールドが次の引数を受け取ると仮定し、これを GraphQL 変数として渡します。
const {graphql} = require('RelayModern');
const storyFragment = graphql`
fragment StoryComponent_story on Story {
comments(
order_by: $orderBy,
filter_mode: $filterMode,
language: $language,
) @connection(key: "StoryComponent_story_comments_connection") {
edges {
nodes {
body {
text
}
}
}
}
}
`;
上記の例では、comments
フィールドをクエリするときに $orderBy
、$filterMode
、および $language
に使用した値はすべて接続識別子の一部になり、Relay ストアから接続レコードにアクセスするときにこれらの値を使用する必要があります。
そのためには、接続を識別するための具体的なフィルター値を指定して、ConnectionHandler.getConnection
に3番目の引数を渡す必要があります。
const {ConnectionHandler} = require('RelayModern');
function updater(store: RecordSourceSelectorProxy) {
const storyRecord = store.get(storyID);
// Get the connection instance for the connection with comments sorted
// by the date they were added
const connectionRecordSortedByDate = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
{order_by: '*DATE_ADDED*', filter_mode: null, language: null}
);
// Get the connection instance for the connection that only contains
// comments made by friends
const connectionRecordFriendsOnly = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
{order_by: null, filter_mode: '*FRIENDS_ONLY*', language: null}
);
}
これは、デフォルトでは、フィルターに使用される値の各組み合わせで、接続に対して異なるレコードが生成されることを意味します。
接続を更新する場合は、変更の影響を受けるすべての関連レコードを更新する必要があります。たとえば、上記の接続の例に新しいコメントを追加する場合、新しいコメントがユーザーの友人によって作成されたものではない場合は、FRIENDS_ONLY
接続にコメントを追加してはいけません。
const {ConnectionHandler} = require('relay-runtime');
function updater(store: RecordSourceSelectorProxy) {
const storyRecord = store.get(storyID);
// Get the connection instance for the connection with comments sorted
// by the date they were added
const connectionRecordSortedByDate = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
{order_by: '*DATE_ADDED*', filter_mode: null, language: null}
);
// Get the connection instance for the connection that only contains
// comments made by friends
const connectionRecordFriendsOnly = ConnectionHandler.getConnection(
storyRecord,
'StoryComponent_story_comments_connection',
{order_by: null, filter_mode: '*FRIENDS_ONLY*', language: null}
);
const newComment = (...);
const newEdge = (...);
ConnectionHandler.insertEdgeAfter(
connectionRecordSortedByDate,
newEdge,
);
if (isMadeByFriend(storyRecord, newComment) {
// Only add new comment to friends-only connection if the comment
// was made by a friend
ConnectionHandler.insertEdgeAfter(
connectionRecordFriendsOnly,
newEdge,
);
}
}
多数のフィルターを使用した接続の管理
ご覧のとおり、接続にいくつかのフィルターを追加するだけで、管理する必要のある接続レコードの複雑さと数が爆発的に増加する可能性があります。これをより簡単に管理するために、Relay は 2 つの戦略を提供します。
1)接続識別子として使用するフィルターを正確に指定します。
デフォルトでは、すべてのページネーション以外のフィルターは、接続識別子の一部として使用されます。ただし、@connection
を宣言するときに、接続の識別に使用するフィルターの正確なセットを指定できます。
const {graphql} = require('relay-runtime');
const storyFragment = graphql`
fragment StoryComponent_story on Story {
comments(
order_by: $orderBy
filter_mode: $filterMode
language: $language
)
@connection(
key: "StoryComponent_story_comments_connection"
filters: ["order_by", "filter_mode"]
) {
edges {
nodes {
body {
text
}
}
}
}
}
`;
@connection
を宣言するときにfilters
を指定することで、Relay に接続の識別の一部として使用するフィルター値の正確なセットを示しています。この場合、language
を除外しています。これは、order_by
およびfilter_mode
の値のみが接続の識別に影響し、新しい接続レコードを生成することを意味します。- 概念的には、これはサーバーからの接続の出力に影響を与える引数、つまり実際にはフィルターである引数を指定していることを意味します。接続の引数の 1 つが、サーバーから返される項目のセットまたは順序を実際に変更しない場合、それは接続に対するフィルターではなく、値が変更されたときに接続を異なる方法で識別する必要はありません。この例では、リクエストするコメントの
language
を変更しても、接続によって返されるコメントのセットは変更されないため、filters
から除外しても安全です。 - これは、接続引数のいずれかがアプリ内で決して変更されないことがわかっている場合にも役立ちます。その場合も
filters
から除外しても安全です。
2)複数のフィルター値を持つ複数の接続を管理するためのより簡単な API の代替手段はまだ保留中です。
後日更新
このページは役に立ちましたか?
簡単な質問に答えて、サイトをさらに改善するためにご協力ください。 いくつかの簡単な質問に回答する.