components with deferred fragment data do not suspend when loadNext is called on a connection
jdk243 opened this issue · 0 comments
We have a table component where fragments for individual cells are deferred with the @defer
directive. On the initial load of the table, we see these cells suspend and fall back to their parent suspense boundary's fallback value. However, on calls to the loadNext
function returned by usePaginationFragment
, we see these cells render and the call to useFragment
reads undefined
rather than causing the component to suspend. However, using the refetch
function returned by usePaginationFragment
I do see the cells with the deferred fragment suspend successfully.
Below is a simplified example of the components and fragments I'm working with when experiencing this behavior:
import React from 'react';
import { usePreloadedQuery, graphql } from 'react-relay';
import { EntryPointComponentProps } from 'data-router';
import { Box, Heading, Separator, Table, Button } from 'shared-components';
import { DeferredPaginationPageReferenceMfeQuery } from './__generated__/DeferredPaginationPageReferenceMfeQuery.graphql';
import { PaginationTableReferenceMfe_company$key as CompanyFragmentKey } from './__generated__/PaginationTableReferenceMfe_company.graphql';
import { TableRowReferenceMfe_journey$key as JourneyFragmentKey } from './__generated__/TableRowReferenceMfe_journey.graphql';
import { DeferredTableCellReferenceMfe_journey$key as JourneyFragmentKey } from './__generated__/DeferredTableCellReferenceMfe_journey.graphql';
type Props = EntryPointComponentProps<{
query: DeferredPaginationPageReferenceMfeQuery;
}>;
const DeferredPaginationPage = ({ queries }: Props) => {
const data = usePreloadedQuery(
graphql`
query DeferredPaginationPageReferenceMfeQuery($companyId: ID!, $first: Int!, $after: String)
@raw_response_type {
company: node(id: $companyId) {
... on Company {
...PaginationTableReferenceMfe_company
...RefetchTableReferenceMfe_company
}
}
}
`,
queries.query
);
if (data.company) {
return (
<Box>
<Heading>Does pagination work with defer?</Heading>
<Separator css={{ my: '$space6' }} />
<React.Suspense fallback="Loading...">
<PaginationTable companyFragmentKey={data.company} />
</React.Suspense>
</Box>
);
}
return <div>something is horribly wrong</div>;
};
const PaginationTable = ({
companyFragmentKey,
}: {
companyFragmentKey: CompanyFragmentKey;
}) => {
const { data, loadNext, refetch } = usePaginationFragment(
graphql`
fragment PaginationTableReferenceMfe_company on Company
@refetchable(queryName: "PaginationTableReferenceMfe_company_refetchable") {
journeys(first: $first, after: $after)
@connection(key: "PaginationTableReferenceMfe_company_journeys") {
edges {
cursor
node {
id
...TableRowReferenceMfe_journey
}
}
totalCount
}
}
`,
companyFragmentKey
);
return (
<React.Suspense fallback="Loading...">
<Table columns={[1, 1, 1]}>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>internal ID</Table.HeaderCell>
<Table.HeaderCell>name</Table.HeaderCell>
<Table.HeaderCell>messagesSent</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
{data.journeys?.edges.map((j) => (
<TableRow journeyFragmentKey={j.node} key={j.node.id} />
))}
</Table.Body>
</Table>
<Button onClick={() => loadNext(1)}>Load next</Button>
<Button
onClick={() =>
refetch({
first: 1,
after: data.journeys?.edges[data.journeys?.edges.length - 1].cursor,
})
}
>
Refetch
</Button>
</React.Suspense>
);
};
const TableRow = ({ journeyFragmentKey }: { journeyFragmentKey: JourneyFragmentKey }) => {
const data = useFragment(
graphql`
fragment TableRowReferenceMfe_journey on Journey {
internalId
name
...DeferredTableCellReferenceMfe_journey
@defer(label: "TableRowReferenceMfe_journey_deferred_cell")
}
`,
journeyFragmentKey
);
return (
<Table.BodyRow>
<Table.BodyCell>{data.internalId}</Table.BodyCell>
<Table.BodyCell>{data.name}</Table.BodyCell>
<React.Suspense fallback="Loading...">
<DeferredTableCell journeyFragmentKey={data} />
</React.Suspense>
</Table.BodyRow>
);
};
const DeferredTableCell = ({
journeyFragmentKey,
}: {
journeyFragmentKey: JourneyFragmentKey;
}) => {
const data = useFragment(
graphql`
fragment DeferredTableCellReferenceMfe_journey on Journey {
stats {
messagesSent
}
}
`,
journeyFragmentKey
);
console.log(data);
return <Table.BodyCell>{data.stats?.messagesSent}</Table.BodyCell>;
};
example responses from queries fired by our calls to load query, load next, and refetch are below
load query:
--END_OF_PART
Content-Type: application/json
{"hasNext":true,"data":{"company":{"__typename":"Company","journeys":{"edges":[{"cursor":"MjAyMy0wOS0wOVQwMzoxODozNy43ODQxOTha","node":{"id":"MDc6Sm91cm5leTE4MDg2","internalId":"18086","name":"Roger Test Stat","__typename":"Journey"}}],"totalCount":5715,"pageInfo":{"endCursor":"MjAyMy0wOS0wOVQwMzoxODozNy43ODQxOTha","hasNextPage":true}},"id":"MDc6Q29tcGFueTU"}},"extensions":{"traceId":"3936810731304619923"}}
--END_OF_PART
Content-Type: application/json
{"hasNext":true,"incremental":[{"data":{"stats":{"messagesSent":31}},"label":"TableRowReferenceMfe_journey$defer$TableRowReferenceMfe_journey_deferred_cell","path":["company","journeys","edges",0,"node"]}]}
--END_OF_PART
Content-Type: application/json
{"hasNext":false}
--END_OF_PART--
load next:
--END_OF_PART
Content-Type: application/json
{"hasNext":true,"data":{"node":{"__typename":"Company","journeys":{"edges":[{"cursor":"MjAyNC0wMi0xMlQxODo0MDozNi41NzE3NDZa","node":{"id":"MDc6Sm91cm5leTMzMTMz","internalId":"33133","name":"Order Shipped 02-12-24 18:40:36","__typename":"Journey"}}],"totalCount":5715,"pageInfo":{"endCursor":"MjAyNC0wMi0xMlQxODo0MDozNi41NzE3NDZa","hasNextPage":true}},"id":"MDc6Q29tcGFueTU"}},"extensions":{"traceId":"3214458784753263795"}}
--END_OF_PART
Content-Type: application/json
{"hasNext":true,"incremental":[{"data":{"stats":{"messagesSent":0}},"label":"TableRowReferenceMfe_journey$defer$TableRowReferenceMfe_journey_deferred_cell","path":["node","journeys","edges",0,"node"]}]}
--END_OF_PART
Content-Type: application/json
{"hasNext":false}
--END_OF_PART--
refetch:
--END_OF_PART
Content-Type: application/json
{"hasNext":true,"data":{"node":{"__typename":"Company","journeys":{"edges":[{"cursor":"MjAyNC0wOC0yNlQyMzoyNzoyMC4wODkwNDVa","node":{"id":"MDc6Sm91cm5leTc3OTI5","internalId":"77929","name":"Some added to cart","__typename":"Journey"}}],"totalCount":5715,"pageInfo":{"endCursor":"MjAyNC0wOC0yNlQyMzoyNzoyMC4wODkwNDVa","hasNextPage":true}},"id":"MDc6Q29tcGFueTU"}},"extensions":{"traceId":"1116799166867392896"}}
--END_OF_PART
Content-Type: application/json
{"hasNext":true,"incremental":[{"data":{"stats":{"messagesSent":0}},"label":"TableRowReferenceMfe_journey$defer$TableRowReferenceMfe_journey_deferred_cell","path":["node","journeys","edges",0,"node"]}]}
--END_OF_PART
Content-Type: application/json
{"hasNext":false}
--END_OF_PART--