Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

components with deferred fragment data do not suspend when loadNext is called on a connection #4803

Open
jdk243 opened this issue Sep 24, 2024 · 0 comments

Comments

@jdk243
Copy link

jdk243 commented Sep 24, 2024

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--
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant