Skip to content

Commit

Permalink
Documentation for observeFragment and waitForFragmentData
Browse files Browse the repository at this point in the history
Reviewed By: tyao1

Differential Revision: D63400528

fbshipit-source-id: f737d8da9ba6c13669aa6746c4e2a6beffe8e573
  • Loading branch information
captbaritone authored and facebook-github-bot committed Sep 27, 2024
1 parent 6a038d0 commit 36eecfe
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 0 deletions.
86 changes: 86 additions & 0 deletions website/docs/api-reference/relay-runtime/observe-fragment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
id: observe-fragment
title: observeFragment
slug: api-reference/observe-fragment
description: Read the value of a fragment and observe it's state and value over time
keywords:
- observable
- fragment
---

import DocsRating from '@site/src/core/DocsRating';

:::warning
`observeFragment` is still an experimental API. It currently has some limitations and may evolve slightly during this phase.
:::

## `observeFragment`

In some cases it can be useful to define data that you wish to read using a GraphQL fragment, but then consume it outside of React render function. `observeFragment` allows you to consume the state and value of a fragment as it changes over time. This includes loading and error states as well as changes to the data as it gets updated by local updates, mutations or updates to Relay's normalized store from other queries.

To read a fragment's data just once, see [`waitForFragmentData`](./wait-for-fragment-data.md).

### Example

```ts
import {observeFragment} from "relay-runtime/experimental";
import { useEffect } from "react";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";

function MyComponent({ key }) {
const user = useFragment(
graphql`
fragment UserFragment on User {
...TitleFragment
}
`,
key,
);

// Update the title as the user's name changes without triggering rerenders.
useEffect(() => {
const subscription = observeFragment(
graphql`
fragment TitleFragment on User {
name
}
`,
user,
).subscribe(result => {
switch(result.kind) {
case "loading":
window.title = "...loading";
break;
case "error":
window.title = "Oops, we hit an error";
break;
case "ok":
window.title = `Welcome ${result.value.name}`;
break;
}
});
return () => {
subscription.unsubscribe();
};
}, [user]);

return <div>Check out the document title!</div>;
}
```

### Arguments

* `environment`: `IEnvironment`. A Relay environment.
* `fragment`: GraphQL fragment specified using a `graphql` template literal.
* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from.
* The type of the fragment reference can be imported from the generated Flow types, from the file `<fragment_name>.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `<fragment_name>$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared.

### Return Value

* An [`Observable`](../../glossary/glossary.md#observable) which returns a discriminated union modeling the three possible states in which a fragment's data might be:
* `{state: 'ok', value: T}` - When data is avalaible the state is `'ok'`. `T` is the data defined in the fragment.
* `{state: 'error': error: Error}` - When the fragment is in an error state either due to network level errors, [`@throwOnFieldError`](../../guides/throw-on-field-error-directive.md) or [`@required(action: THROW)`](../../guides/required-directive.md) field errors.
* `{state: 'loading'}` - When the parent request, or current `@defer` payload is still in flight, or a [`@live` Relay Resolver](../../guides/relay-resolvers/live-fields.md) being read is in a suspended state.

<DocsRating />
89 changes: 89 additions & 0 deletions website/docs/api-reference/relay-runtime/wait-for-fragment-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
id: wait-for-fragment-data
title: waitForFragmentData
slug: /api-reference/wait-for-fragment-data/
description: Read the value of a fragment as a promise
keywords:
- promise
- fragment
---

import DocsRating from '@site/src/core/DocsRating';

:::warning
`waitForFragmentData` is still an experimental API. It currently has some limitations and may evolve slightly during this phase.
:::

## `waitForFragmentData`

In some cases it can be useful to define data that you wish to read using a GraphQL fragment, but then consume it just once outside of React render function. `waitForFragmentData` allows you to wait for the data of a fragment to be avalaible,

To read a fragment's data as it changes over time, see [`observeFragment`](./observe-fragment.md).

### Example: Deferring data used in an event handler

One use case for `waitForFragmentData` is to defer fetching data that is needed inside an event handler, but is not needed to render the initial view.

```tsx
import { useCallback } from "react";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";
import { waitForFragmentData } from "relay-runtime/experimental";

function MyComponent({ key }) {
const user = useFragment(
graphql`
fragment UserFragment on User {
name
# Page load can complete before this data has streamed in from the server.
...EventHandlerFragment @defer
}
`,
key,
);

const onClick = useCallback(async () => {
// Once the user clicks, we may need to wait for the data to finish loading.
const userData = await waitForFragmentData(
graphql`
fragment EventHandlerFragment on User {
age
}
`,
user,
);

if (userData.age < 10) {
alert("Hello kiddo!");
} else if (userData.age < 18) {
alert("Hello young person!");
} else {
alert("Hello adult person!");
}
}, [user]);

return (
<div>
My name is {user.name}
<button onClick={onClick}>Greet</button>
</div>
);
}
```

### Arguments

* `environment`: `IEnvironment`. A Relay environment.
* `fragment`: GraphQL fragment specified using a `graphql` template literal.
* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from.
* The type of the fragment reference can be imported from the generated Flow types, from the file `<fragment_name>.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `<fragment_name>$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared.

### Return Value

* A `Promise<T>` where `T` is the data defined in the fragment.

The Promise will wait for all network data to become avaliable as well as any [`@live` Relay Resolver](../../guides/relay-resolvers/live-fields.md) to be in a non-suspended state before it resolves.

In the case of a network error, or a field-level error due to [`@throwOnFieldError`](../../guides/throw-on-field-error-directive.md) or [`@required(action: THROW)`](../../guides/required-directive.md), the Promise will reject with an error.

<DocsRating />
2 changes: 2 additions & 0 deletions website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ module.exports = {
'api-reference/relay-runtime/store',
'api-reference/relay-runtime/commit-mutation',
'api-reference/relay-runtime/request-subscription',
'api-reference/relay-runtime/observe-fragment',
'api-reference/relay-runtime/wait-for-fragment-data',
],
},
{
Expand Down

0 comments on commit 36eecfe

Please sign in to comment.