Rendering List Data and Pagination (part 2)
Relay 02/18
Updating Connections
connectioně ë ëë§ íë ¤ęł í ë, ěŹěŠěě ëěě ë°ëź ěě´í ě connectioně ěśę°íęą°ë ěě íęł ěśě ěë ěë¤. ě´ě Updating Data ěšě ěě Relayë ě ęˇíë ëĄěťŹ in-memory ě ěĽě뼟 ę°ęł ěęł , ꡸ ě ěĽěěë IDëĄ ęľŹëśë늰 ě ęˇíë GraphQL ë°ě´í°ę° ě ěĽëěë¤.
Relay뼟 íľí´ mutation, subscription íšě ëĄěťŹ ë°ě´í°ëĽź ě
ë°ě´í¸ í 경ě°ě 돴쥰깴 ë°ě´í°ě ě ꡟíęł ě˝ęł ě°ë ë´ěŠě íŹí¨íë updater
í¨ě뼟 꾏íí´ ě ęłľí´ěź íë¤. ë ě˝ëę° ě
ë°ě´í¸ë 경ě°, ě
ë°ě´í¸ë ë°ě´í°ě ěíĽě ë°ë ěť´íŹëí¸ë¤ě ě ëś ě´ëĽź ěěě°¨ë ¤ ë¤ě ë ëë§ëë¤.
Connection Records
Relayěě @connection
ěźëĄ ë§íšë connection íëë ě ěĽě ë´ëśě íšëłí ë ě˝ëëĄě ě ěĽëęł , ěŹíęšě§ fetchë 모ë ë°ě´í°ë¤ě ě ěĽí´ëë¤. connectioněźëĄëśí° ë°ě´í°ëĽź ěśę°íęą°ë ěě í기 ěí´ěë connection key
뼟 íľí´ connection ë ě˝ëě ě ꡟí´ěź íëë°, ě´ keyë @connection
ě ě¸ě í ë ě ęłľëë¤. íší, key
ë ConnectionHandler
API뼟 ěŹěŠí ë updater
í¨ě ë´ëśěě connectioně ě ꡟí ě ěëëĄ í´ě¤ë¤.
ě뼟 ë¤ě´, ěëě fragmentë @connection
ě ě¸ě´ ëě´ě기 ë돸ě updater
í¨ě ë´ëśěě connection ë ě˝ëě ě ꡟíë ę˛ě´ ę°ëĽíë¤.
const {graphql} = require('react-relay');
const storyFragment = graphql`
fragment StoryComponent_story on Story {
comments @connection(key: "StoryComponent_story_comments_connection") {
nodes {
body {
text
}
}
}
}
`;
Accessing connections using __id
__id
connectioně __id
íë뼟 쿟댏í í, ě´ëĽź íľí´ ě ěĽěě ë ě˝ëě ě ꡟí ě ěë¤.
const fragmentData = useFragment(
graphql`
fragment StoryComponent_story on Story {
comments @connection(key: "StoryComponent_story_comments_connection") {
# __id íë뼟 쿟댏íë¤.
__id
# ...
}
}
`,
props.story,
);
// connectioně ë ě˝ë id뼟 ę°ě ¸ě¨ë¤.
const connectionID = fragmentData?.comments?.__id;
ě´í connectionID
뼟 íľí´ ě ěĽěě ë ě˝ëě ě ꡟí ě ěę˛ ëë¤.
function updater(store: RecordSourceSelectorProxy) {
// connectionID is passed as input to the mutation/subscription
const connection = store.get(connectionID);
// ...
}
죟ě : GraphQL APIę°
__id
íë뼟 ë ¸ěśěíŹ íěë ěë¤.__id
ë Relayę° connection ë ě˝ë뼟 꾏ëśí기 ěí´ ěëěźëĄ ěśę°í ę˛ě´ë¤.
Accessing connections using ConnectionHandler.getConnectionID
ConnectionHandler.getConnectionID
ë§ě˝ connectioně ę°ęł ěë ëśëި ë ě˝ëě connection IDě ě ꡟí ě ěë¤ëŠ´, ConnectionHandler.getConnectionID
API뼟 íľí´ connection ë ě˝ëě ě ꡟí ě ěę˛ ëë¤.
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);
// ...
}
Accessing connections using ConnectionHandler.getConnection
ConnectionHandler.getConnection
ë§ě˝ connectioně ę°ęł ěë ëśëި ë ě˝ëě ě ꡟí ě ěë¤ëŠ´, ConnectionHandler.getConnection
APIě ꡸ ëśëި ë ě˝ë뼟 íľí´ connection ë ě˝ëě ě ꡟí ě ěë¤.
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',
);
// ...
}
Adding edges
connectioně edge뼟 ěśę°í ě ěë ëŞę°ě§ ëěë¤ě´ ěë¤.
Using declarative directives
ëł´íľ mutationě´ë subscriptioně íě´ëĄëë ë¨ěź edgeë edgeë¤ě 댏ě¤í¸ě í¨ęť ěë˛ěě ěśę°ë ěëĄě´ edgeë¤ě ë
¸ěśěí¨ë¤. ë§ě˝ mutation íšě subscriptioně´ responseěě 쿟댏í ě ěë edge íšě edgeë¤ě ë
¸ěśěí¨ë¤ëŠ´, ěëĄ ěěąë edge뼟 ě§ě ë connectioně ěśę°í기 ěí´ @appendEdge
íšě @prependEdge
ě ě¸ě í ě ěë¤.
ëë mutationě´ë subscriptioně íě´ëĄëę° íëě nodeë nodeë¤ě 댏ě¤í¸ě í¨ęť ěë˛ěě íëëĄ ěśę°ë ěëĄě´ ë
¸ëë¤ě ë
¸ěśěíŹ ěë ěë¤. ěěě edge뼟 ë
¸ěśěí¨ę˛ęłź ë§ě°Źę°ě§ëĄ, mutationě´ë subscriptioně´ ěëľěě ěëĄě´ ë
¸ëë ë
¸ëë¤ě íë뼟 ë
¸ěśěí¨ë¤ëŠ´ @appendNode
íšě @prependNode
ě ě¸ě íľí´ ěëĄ ě§ě ë ë
¸ëë¤ě ě§ě ë connectioně ěśę°ěíŹ ě ěë¤.
ě´ ě ě¸ě connections
íëźëݏí°ëĽź ë°ëë°, ě´ë connection IDë¤ě ë°°ě´ëĄ ę°ęł ěë GraphQL ëłěě´ë¤. Connection IDë¤ě connectioně __id
íëěě ěťě´ě§ęą°ë ConnectionHandler.getconnectionID
API뼟 íľí´ ěťě ě ěë¤.
@appendEdge
/ @prependEdge
ě´ ě ě¸ë¤ě íëě edge íšě ěŹëŹ edgeë¤ě´ ë´ę¸´ 댏ě¤í¸ěě ëěíë¤. @prependEdge
ë ě íë edgeë¤ě ę° connectionë¤ě 맨 ěě ěśę°ěí¤ęł , @appendEdge
ë ě íë edgeë¤ě ę° connectionë¤ě 맨 ë¤ě ěśę°ěí¨ë¤.
ě¸ě
connections
: connection IDë¤ě ë°°ě´.edgeTypeName
: node뼟 íŹí¨íë edgeě íě ě´ëŚ.ConnectionHandler.createEdge
ě edge íě ě¸ěě ëěźíë¤.
// 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],
},
});
Manually adding edges
ěě ě ě¸ëʏě connectioněě í몊ë¤ě ěëěźëĄ ěśę° íšě ě ęą°í íěěąě íŹę˛ ě ęą°íě§ë§, ěŹě¸í ě ě´ë ëśę°ëĽí기 ë돸ě 모ë ěŹëĄëĽź 윊쥹ěí¤ě§ ěě ěë ěë¤.
connectioně ěě íë updater뼟 ěěąíë ¤ëŠ´ connection ë ě˝ëě ëí ě ꡟ ęśíě´ ěëě§ íě¸í´ěź íë¤. connection ë ě˝ëę° ěěźëŠ´ connectioně ěśę°íë ¤ë ě edgeě ëí ë ě˝ëë íěíë¤. ěźë°ě ěźëĄ mutation íšě subscriptioně íě´ëĄëěë ěśę°ë ěëĄě´ edgeę° íŹí¨ëë¤. ꡸ë ě§ ěěźëŠ´ ě˛ěëśí° ě edge뼟 꾏ěąí ěë ěë¤.
ě뼟 ë¤ě´ ë¤ě mutationě responseěě ěëĄ ěěąë edge뼟 쿟댏í ě ěë¤.
const {graphql} = require('react-relay');
const createCommentMutation = graphql`
mutation CreateCommentMutation($input: CommentCreateData!) {
comment_create(input: $input) {
comment_edge {
cursor
node {
body {
text
}
}
}
}
}
`;
ěëĄě´ edge뼟 ę°ě ¸ě¤ę¸° ěí´
cursor
뼟 ę°ě ¸ě¨ë¤ë ě ě 죟몊í´ěź íë¤. 돴쥰깴 íěí ę˛ě ěëě§ë§cursor
기ë°ě íě´ě§ë¤ě´ě ě ěíí ę˛ě´ëźëŠ´ íěíë¤.
updater
ë´ëśěěë Relayě ě ěĽě APIě mutation ěëľě ě´ěŠí´ edgeě ě ꡟí ě ěë¤.
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,
);
// ...
}
mutation íě´ëĄëë
store.getRootField
ëŠěë뼟 íľí´ ě˝ě´ě ě ě ěĽěěě ëŁ¨í¸ íëëĄě¨ ěŹěŠ ę°ëĽíë¤. ě´ ěěěěëcomment_create
뼟 ě˝ęł ěë¤.ě ěěěěë ěë˛ëĄëśí° ë°ěě¨ ë°ěě¨ edgeě
ConnectionHandler.buildConectionEdge
ëŠěë뼟 ě´ěŠí´ ě edge뼟 ěśę°íë¤.
ě edge뼟 ěśę°í기 ěí´ě 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 */
);
// ...
}
íë˛ ě edgeě ë ě˝ë뼟 ěťě´ě¨ ě´íëĄë ConnectionHandler.insertEdgeAfter
íšě ConnectionHandler.insertEdgeBefore
뼟 ě´ěŠí´ ě edge뼟 connectioně ěśę°í ě ěë¤.
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,
);
}
Removing edges
Using the declarative deletion directive
edge뼟 ěśę°íëę˛ęłź ě ěŹíę˛ @deleteEdge
ě ě¸ě íľí´ edge뼟 connectioněźëĄëśí° ě ęą°í ě ěë¤. mutationě´ë subscriptioně´ responseěě 쿟댏í ě ěë ěě ë ë
¸ëě IDę° ěë íë뼟 ë
¸ěśíë ę˛˝ě° @deleteEdge
ě§ě돸ě ě´ěŠí´ connectioněě ę° edge뼟 ěě í ě ěë¤.
@deleteEdge
ID
íšě [ID]
뼟 ë°ííë GraphQL íëěě ěŹěŠě´ ę°ëĽíë¤. ëěźí id
뼟 ę°ě§ ěě§ëĽź íŹí¨íë ë
¸ëë¤ě connections
ë°°ě´ěě ěě íë¤.
ě¸ě
connections
: connection IDë¤ě ë°°ě´
// 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],
},
});
Manually removing edges
ConnectionHandler
ë ConnectionHandler.deleteNode
ëŠěë뼟 íľí´ connectioněźëĄëśí°ě edge ěě 뼟 ě§ěíë¤.
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뼟 íľí´ edge뼟 ěě íë¤. ěŚ, ě´ë¤ nodeę° ěŁźě´ě§ idě í´ëšíë edge뼟 ę°ęł ěëě§ëĽź íě´ëł´ęł , ꡸ edge뼟 ěě íë¤.
Connection identity with filters
connectioně ě ě¸í ë filter ě¸ě뼟 ě ëŹí늴 filterě ěŹěŠë ę°ë¤ě connection ěëłěě ěźëśę° ëë¤. ěŚ, Relay ě ěĽěěě connectioně ěëłí기 ěí ę°ěźëĄ ěŹěŠëë¤.
ěë ěěěě comments
íëę° ë¤ěęłź ę°ě ě¸ě뼟 ë°ëë¤ęł íě.
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
뼟 ě¸ěëĄ ë°ěźëа comments
뼟 쿟댏í ë ěëłěëĄě ëěíë¤. ě´í connection ë ě˝ëě ě ꡟí기 ěí´ěë ě ę°ë¤ě ë겨죟ě´ěź íë¤.
ę°ě ě´ě ëĄ ConnectionHandler.getConnection
ëŠěë뼟 ě´ěŠí ëë ě¸ë˛ě§¸ ě¸ěëĄ ě ę°ë¤ě ë겨죟ě´ěź íë¤.
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*', langugage: null}
);
}
ě¸ěëĄ ëę˛¨ě¤ ę° ëłěë¤ě 쥰íŠě´ íí°ëĄ ěěŠí´ ë¤ëĽ¸ ë ě˝ë뼟 댏í´íë¤ë ę˛ě ěěíë¤.
connectioně ě
ë°ě´í¸ íë ¤ęł í ę˛˝ě° ęˇ¸ ě
ë°ě´í¸ě ěíĽě ë°ë 모ë ë ě˝ëë¤ě ě
ë°ě´í¸ í´ěź íë¤. ě뼟 ë¤ě´, ě ëę¸ě íë ěśę°íë ¤ í ę˛˝ě° ěŹěŠěě ěšęľŹëĄëśí° ë§ë¤ě´ě§ ëę¸ě´ ěëëźëŠ´ FRIENDS_ONLY
connectioně ëę¸ě´ ěśę° ëě´ěë ěëë¤.
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,
);
}
}
ěŹëŹ íí°ë¤ě ě´ěŠí´ connectionë¤ě ę´ëŚŹíë¤. ꡸ěšë§ ë¨ěí ëŞę°ě íí° ěĄ°íŠë§ěźëĄ ëłľěĄëę° íŹę˛ ěŚę°íë 돸ě ě ě´ ěë¤.
ě´ëĽź í´ę˛°í기 ěí´ Relayë ëę°ě§ ě ëľě ěŹěŠíë¤.
ě´ë¤ íí°ę° connection ěëłěëĄ ěŹěŠëëě§ ě ííę˛ íšě íë¤.
íě´ě§ë¤ě´ě ě ě°ě´ë íí°ë§
@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 } } } } } `;
languageë íě´ě§ë¤ě´ě ě ěŹěŠëë íí°ę° ěë기 ë돸ě
@connection
ë´ëśě ëŁě´ěŁźě§ ěěë¤.ę°ë ě ěźëĄë ě´ë¤ ě¸ěë§ ěë˛ëĄëśí° ë°ěě¨ connectioně 결곟ě ěíĽě 미ěšëě§ ě í´ě¤ ę˛ě´ë¤. ë§ě˝ ě´ë¤ ě¸ěę° ěë˛ëĄëśí° ë°ěě¨ connectioně 결곟ë ě ë Źë°Šěě ěíĽě ěŁźě§ ěëë¤ëŠ´ ëšźë ę´ě°Žě ě¸ěëźë ëťě´ë¤. ě ěěěěë
language
ę° ęˇ¸ë ë¤.ěąě´ ě¤íë¨ě ěě´ ě´ë¤ ëśëśë ëłę˛˝íě§ ěë ě¸ěę° ěë¤ëŠ´ íí°ěě ě§ěë ěě íë¤ë ëťě´ ëë¤.
ë ěŹě´ ëěě ěě§ ěĄ´ěŹíě§ ěëë¤.
Advanced Pagination
ě´ë˛ ěšě
ěěë usePaginationFragment
뼟 ě´ěŠí ęł ę¸ íě´ě§ë¤ě´ě
꾏í ë°Šë˛ë¤ě ěě본ë¤.
Pagination Over Multiple Connections
ę°ě ěť´íŹëí¸ěě ěŹëŹę°ě connectionë¤ě ě´ěŠí´ íě´ě§ë¤ě´ě
í기 ěí´ěë usePaginationFragment
뼟 ěŹëŹë˛ ěŹěŠí늴 ëë¤.
import type {CombinedFriendsListComponent_user$key} from 'CombinedFriendsListComponent_user.graphql';
import type {CombinedFriendsListComponent_viewer$key} from 'CombinedFriendsListComponent_viewer.graphql';
const React = require('React');
const {graphql, usePaginationFragment} = require('react-relay');
type Props = {
user: CombinedFriendsListComponent_user$key,
viewer: CombinedFriendsListComponent_viewer$key,
};
function CombinedFriendsListComponent(props: Props) {
const {data: userData, ...userPagination} = usePaginationFragment(
graphql`
fragment CombinedFriendsListComponent_user on User {
name
friends
@connection(
key: "CombinedFriendsListComponent_user_friends_connection"
) {
edges {
node {
name
age
}
}
}
}
`,
props.user,
);
const {data: viewerData, ...viewerPagination} = usePaginationFragment(
graphql`
fragment CombinedFriendsListComponent_user on Viewer {
actor {
... on User {
name
friends
@connection(
key: "CombinedFriendsListComponent_viewer_friends_connection"
) {
edges {
node {
name
age
}
}
}
}
}
}
`,
props.viewer,
);
return (...);
}
ě´ë ę˛ í늴 ë기ë íě§ë§ Relayěěë íëě ěť´íŹëí¸ëš íëě connectioně ě´ěŠíë ę˛ě ěśě˛íë¤.
Bi-directional Pagination
Pagination ěšě
ěě ě´ëťę˛ usePaginationFragment
뼟 ěŹěŠí´ ěźë°Šě ě¸ ë°ŠíĽ(forward)ě íě´ě§ë¤ě´ě
ě íëě§ ěěëł´ěë¤. íě§ë§ connectioně ěë°ŠíĽ(backward) íě´ě§ë¤ě´ě
ë ě§ěíë¤. forwardě backwardë connectioně ë´ěŠě´ ě´ëťę˛ ě ë Źëëě§ëĽź ě미íë¤. ě뼟 ë¤ě늴 âforwardâë ěľě ě, âbackwardâë ě¤ëë ěě´ ë ę˛ě´ë¤.
ë°ŠíĽě ě미ěë ëłę°ëĄ, Relayë usePaginationFragment
뼟 ě´ěŠí´ ěë°ŠíĽ íě´ě§ë¤ě´ě
ě ěí API뼟 ëłëëĄ ě§ěí기ë íë¤. before
, last
ë after
, first
ě í¨ęť ěŹěŠëë¤.
import type {FriendsListComponent_user$key} from 'FriendsListComponent_user.graphql';
const React = require('React');
const {Suspense} = require('React');
const {graphql, usePaginationFragment} = require('react-relay');
type Props = {
userRef: FriendsListComponent_user$key,
};
function FriendsListComponent(props: Props) {
const {
data,
loadPrevious,
hasPrevious,
// ... forward pagination values
} = usePaginationFragment(
graphql`
fragment FriendsListComponent_user on User {
name
friends(after: $after, before: $before, first: $first, last: $last)
@connection(key: "FriendsListComponent_user_friends_connection") {
edges {
node {
name
age
}
}
}
}
`,
userRef,
);
return (
<>
<h1>Friends of {data.name}:</h1>
<List items={data.friends?.edges.map(edge => edge.node)}>
{node => {
return (
<div>
{node.name} - {node.age}
</div>
);
}}
</List>
{hasPrevious ? (
<Button onClick={() => loadPrevious(10)}>
Load more friends
</Button>
) : null}
{/* Forward pagination controls can go simultaneously here */}
</>
);
}
ë¨ěí ëŞ ëŞ íë ë°Šěë§ ë¤ëĽ¸ę˛ě´ęł âforwardâ, âbackwardâę° ě미íë ë°ë after, first뼟 ě´ěŠí ë°Šěęłź ěě í ę°ë¤.
âforwardâ, âbackwardâ뼟 ěŹěŠíë íě´ě§ë¤ě´ě ě ě ě ë
usePaginationFragment
뼟 í ë˛ë§ í¸ěśíë ę˛ě´ë¤. ë°ëźě ëěźí ěť´íŹëí¸ ë´ěě âforwardâě âbackawardâë ëěě ěíë ě ěë¤.
Custom Connection State
기본ě ěźëĄ usePaginationFragment
ě @connection
ě ěŹěŠí ë, Relayë âforwardâě¸ ę˛˝ě° ěëĄě´ íě´ě§ëĽź connectioně ë¤ě ěśę°íęł , âbackwardâě¸ ę˛˝ě° ě íě´ě§ëĽź ěě ěśę°íë¤. ěŚ, ěť´íŹëí¸ë íě íě´ě§ë¤ě´ě
ě íľí´ ěśě ë 모ë ë°ě´í°ě, mutationě´ë subscriptioně íľí´ ěśę°ëęą°ë ěě ë 결곟ëĄěě connectioně ë ëë§íë¤.
꡸ëŹë, íě´ě§ë¤ě´ě 결곟뼟 ëłíŠíęł ěśě ěíŹ ë ë¤ëĽ´ę˛ ëěíë ę˛ě ë°ë ěë ěęł , ëĄěťŹ ěť´íŹëí¸ě ěí뼟 connectioně ë°ěěí¤ęł ěśě ěë ěë¤.
connectioně visible sliceë windowę° ëŹëźě§ë ę˛ě ěśě íë ¤ í ë
ěę°ě ěźëĄ íě´ě§ëĽź ëśëŚŹíë ¤ í ë. ě´ ëë ę° íě´ě§ě ě íí ě´ë¤ ěě´í ë¤ě´ ë¤ě´ę°ěź íëě§ě ëí ě§ěě´ íěíë¤.
ëěźí connectioně ěëĄ ë¤ëĽ¸ âë"(ë§ě§ë§ ë ¸ë)ě íěí늴ě ęˇ¸ë¤ ěŹě´ě gapě ěśě íęł , gap ěŹě´ě íě´ě§ë¤ě´ě ě ěíí ë 결곟뼟 ëłíŠí ě ěë¤.
ě뼟 ë¤ě´, ę°ěĽ ě¤ëë ëę¸ě´ 맨 ěě íěëë ëę¸ ëŞŠëĄě ë ëë§íë ¤ íęł , ë¤ě íě´ě§ëĽź 매기기 ěí´ěë ěí¸ěěŠ í ě ěë gapě´ íěíë¤.
ě´ë° ëłľěĄí ěŹěŠěŹëĄë¤ě í´ę˛°í기 ěí´ Relayěěë ę°ë°ě´ ęłě ě§íě¤ě´ë¤.
Refreshing connections
Prefetching pages of a Connection
Rendering One Page of Items at a Time
Last updated