-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Add documentation for useSuspenseFragment
#12356
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 <li>{complete ? data.text : "incomplete"}</li>; | ||
|
@@ -573,7 +573,7 @@ function Item(props) { | |
const { complete, data } = useFragment({ | ||
fragment: ITEM_FRAGMENT, | ||
fragmentName: "ItemFragment", | ||
from: item | ||
from: props.item | ||
}); | ||
|
||
return <li>{complete ? data.text : "incomplete"}</li>; | ||
|
@@ -589,6 +589,71 @@ function Item(props) { | |
|
||
See the [API reference](../api/react/hooks#usefragment) for more details on the supported options. | ||
|
||
<MinVersion version="3.13.0"> | ||
## `useSuspenseFragment` | ||
</MinVersion> | ||
|
||
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. | ||
jerelmiller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```tsx | ||
import { useSuspenseFragment } from "@apollo/client"; | ||
|
||
function Item(props) { | ||
const { data } = useSuspenseFragment({ | ||
fragment: ITEM_FRAGMENT, | ||
fragmentName: "ItemFragment", | ||
from: props.item | ||
}); | ||
|
||
return <li>{data.text}</li>; | ||
} | ||
``` | ||
|
||
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: | ||
jerelmiller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```tsx | ||
function List() { | ||
const { data } = useSuspenseQuery(listQuery); | ||
|
||
return ( | ||
<ol> | ||
{data.list.map(item => ( | ||
<Suspense fallback={<Spinner />}> | ||
<Item key={item.id} item={item}/> | ||
</Suspense> | ||
))} | ||
</ol> | ||
); | ||
} | ||
``` | ||
|
||
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`. | ||
jerelmiller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
<Note> | ||
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. | ||
</Note> | ||
|
||
### 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. | ||
|
||
<Caution> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know we are trying to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, definitely something worth cautioning! |
||
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. | ||
jerelmiller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</Caution> | ||
|
||
<MinVersion version="3.12.0"> | ||
## Data masking | ||
</MinVersion> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noticed this typo when reading through so I went ahead and fixed this.