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