From 6b349c1ceb66ead0620d676031242371c3095fd8 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Fri, 7 Feb 2025 11:59:03 -0700 Subject: [PATCH 1/5] Fix typo in useFragment --- docs/source/data/fragments.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/data/fragments.mdx b/docs/source/data/fragments.mdx index d8f4fdf4373..39aec60e677 100644 --- a/docs/source/data/fragments.mdx +++ b/docs/source/data/fragments.mdx @@ -561,7 +561,7 @@ function Item(props: { item: { __typename: 'Item', id: number }}) { const { complete, data } = useFragment({ fragment: ItemFragment, fragmentName: "ItemFragment", - from: item + from: props.item }); return
  • {complete ? data.text : "incomplete"}
  • ; @@ -573,7 +573,7 @@ function Item(props) { const { complete, data } = useFragment({ fragment: ITEM_FRAGMENT, fragmentName: "ItemFragment", - from: item + from: props.item }); return
  • {complete ? data.text : "incomplete"}
  • ; From 8f8bbe2b0ec6577883982e1641ccfaf99ec8f664 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Fri, 7 Feb 2025 12:41:34 -0700 Subject: [PATCH 2/5] First pass at `useSuspenseFragment` docs --- docs/source/data/fragments.mdx | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/source/data/fragments.mdx b/docs/source/data/fragments.mdx index 39aec60e677..a45af16bb89 100644 --- a/docs/source/data/fragments.mdx +++ b/docs/source/data/fragments.mdx @@ -589,6 +589,71 @@ function Item(props) { See the [API reference](../api/react/hooks#usefragment) for more details on the supported options. + +## `useSuspenseFragment` + + +For those that have integrated with React [Suspense](https://react.dev/reference/react/Suspense), `useSuspenseFragment` is available as a drop-in replacement for `useFragment`. `useSuspenseFragment` works identically to `useFragment` but will suspend while `data` is incomplete. + +Let's update the example from the previous section to use `useSuspenseFragment`. First we'll update our `Item` component and replace `useFragment` with `useSuspenseFragment`. Since we are using Suspense, we no longer have to check for a `complete` property to determine if the result is fully complete because the component will suspend otherwise. + +```tsx +import { useSuspenseFragment } from "@apollo/client"; + +function Item(props) { + const { data } = useSuspenseFragment({ + fragment: ITEM_FRAGMENT, + fragmentName: "ItemFragment", + from: props.item + }); + + return
  • {data.text}
  • ; +} +``` + +Next we'll will wrap our `Item` components in a `Suspense` boundary to show a loading indicator if the data from `ItemFragment` is not complete. Since we're using Suspense, we'll replace `useQuery` with `useSuspenseQuery` as well: + +```tsx +function List() { + const { data } = useSuspenseQuery(listQuery); + + return ( +
      + {data.list.map(item => ( + }> + + + ))} +
    + ); +} +``` + +And that's it! Suspense made our `Item` component a bit more succinct since we no longer need to check the `complete` property to determine if we can use safely use `data`. + + +In most cases, `useSuspenseFragment` will not suspend when rendered as a child of a query component. In this example `useSuspenseQuery` loads the full query data before each `Item` is rendered so the `data` inside each fragment is already complete. The `Suspense` boundary in this example ensures that a loading spinner is shown if field data is removed for the cache for any given item in the list, such as when a manual cache update is performed. + + +### Using `useSuspenseFragment` with `@defer` + +`useSuspenseFragment` is helpful when combined with the [`@defer` directive](./directives#defer) to show a loading state while the fragment data is streamed to the query. Let's update our `GetItemList` query to defer loading the `ItemFragment`'s fields. + +```graphql +query GetItemList { + list { + id + ...ItemFragment @defer + } +} +``` + +Our list will now render as soon as our list returns but before the data for `ItemFragment` is loaded. + + +You **must** ensure that any key fields used to identify the object passed to the `from` option are not deferred, otherwise you risk suspending the `useSuspenseFragment` hook forever. If you need to defer loading key fields for any reason, the component itself will need to be conditionally rendered until the object passed to the `from` option is identifiable by the cache. + + ## Data masking From 399fbd6b06d50dd178065a4c2284d282952f4a6a Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Fri, 7 Feb 2025 13:02:19 -0700 Subject: [PATCH 3/5] Fix formatting --- docs/source/data/fragments.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/data/fragments.mdx b/docs/source/data/fragments.mdx index a45af16bb89..1912bdeee53 100644 --- a/docs/source/data/fragments.mdx +++ b/docs/source/data/fragments.mdx @@ -618,13 +618,13 @@ function List() { const { data } = useSuspenseQuery(listQuery); return ( -
      - {data.list.map(item => ( - }> - - - ))} -
    +
      + {data.list.map(item => ( + }> + + + ))} +
    ); } ``` From 511f2796662b4d972f7997d227d17d8892a4c1bd Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Tue, 11 Feb 2025 08:56:16 -0700 Subject: [PATCH 4/5] Minor tweak to order of sentence --- docs/source/data/fragments.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/data/fragments.mdx b/docs/source/data/fragments.mdx index 1912bdeee53..eaba18d2bbf 100644 --- a/docs/source/data/fragments.mdx +++ b/docs/source/data/fragments.mdx @@ -632,7 +632,7 @@ function List() { And that's it! Suspense made our `Item` component a bit more succinct since we no longer need to check the `complete` property to determine if we can use safely use `data`. -In most cases, `useSuspenseFragment` will not suspend when rendered as a child of a query component. In this example `useSuspenseQuery` loads the full query data before each `Item` is rendered so the `data` inside each fragment is already complete. The `Suspense` boundary in this example ensures that a loading spinner is shown if field data is removed for the cache for any given item in the list, such as when a manual cache update is performed. +In most cases, `useSuspenseFragment` will not suspend when rendered as a child of a query component. In this example `useSuspenseQuery` loads the full query data before each `Item` is rendered so the `data` inside each fragment is already complete. The `Suspense` boundary in this example ensures that a loading spinner is shown if field data is removed for any given item in the list in the cache, such as when a manual cache update is performed. ### Using `useSuspenseFragment` with `@defer` From 2af31d07031905ddbdf55d650258140bb19e5b70 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Tue, 11 Feb 2025 09:44:43 -0700 Subject: [PATCH 5/5] Update based on feedback Co-authored-by: Maria Elisabeth Schreiber --- docs/source/data/fragments.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/data/fragments.mdx b/docs/source/data/fragments.mdx index eaba18d2bbf..4124d0eb53a 100644 --- a/docs/source/data/fragments.mdx +++ b/docs/source/data/fragments.mdx @@ -595,7 +595,7 @@ See the [API reference](../api/react/hooks#usefragment) for more details on the For those that have integrated with React [Suspense](https://react.dev/reference/react/Suspense), `useSuspenseFragment` is available as a drop-in replacement for `useFragment`. `useSuspenseFragment` works identically to `useFragment` but will suspend while `data` is incomplete. -Let's update the example from the previous section to use `useSuspenseFragment`. First we'll update our `Item` component and replace `useFragment` with `useSuspenseFragment`. Since we are using Suspense, we no longer have to check for a `complete` property to determine if the result is fully complete because the component will suspend otherwise. +Let's update the example from the previous section to use `useSuspenseFragment`. First, we'll update our `Item` component and replace `useFragment` with `useSuspenseFragment`. Since we are using Suspense, we no longer have to check for a `complete` property to determine if the result is complete because the component will suspend otherwise. ```tsx import { useSuspenseFragment } from "@apollo/client"; @@ -611,7 +611,7 @@ function Item(props) { } ``` -Next we'll will wrap our `Item` components in a `Suspense` boundary to show a loading indicator if the data from `ItemFragment` is not complete. Since we're using Suspense, we'll replace `useQuery` with `useSuspenseQuery` as well: +Next, we'll will wrap our `Item` components in a `Suspense` boundary to show a loading indicator if the data from `ItemFragment` is not complete. Since we're using Suspense, we'll replace `useQuery` with `useSuspenseQuery` as well: ```tsx function List() { @@ -629,7 +629,7 @@ function List() { } ``` -And that's it! Suspense made our `Item` component a bit more succinct since we no longer need to check the `complete` property to determine if we can use safely use `data`. +And that's it! Suspense made our `Item` component a bit more succinct since we no longer need to check the `complete` property to determine if we can safely use `data`. In most cases, `useSuspenseFragment` will not suspend when rendered as a child of a query component. In this example `useSuspenseQuery` loads the full query data before each `Item` is rendered so the `data` inside each fragment is already complete. The `Suspense` boundary in this example ensures that a loading spinner is shown if field data is removed for any given item in the list in the cache, such as when a manual cache update is performed. @@ -651,7 +651,7 @@ query GetItemList { Our list will now render as soon as our list returns but before the data for `ItemFragment` is loaded. -You **must** ensure that any key fields used to identify the object passed to the `from` option are not deferred, otherwise you risk suspending the `useSuspenseFragment` hook forever. If you need to defer loading key fields for any reason, the component itself will need to be conditionally rendered until the object passed to the `from` option is identifiable by the cache. +You **must** ensure that any key fields used to identify the object passed to the `from` option are not deferred. If they are, you risk suspending the `useSuspenseFragment` hook forever. If you need to defer loading key fields, conditionally render the component until the object passed to the `from` option is identifiable by the cache.