relay-runtime 20.1.1 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/experimental.js +1 -1
- package/experimental.js.flow +8 -8
- package/handlers/connection/ConnectionHandler.js.flow +5 -5
- package/handlers/connection/ConnectionInterface.js.flow +1 -1
- package/index.js +1 -1
- package/index.js.flow +125 -62
- package/lib/experimental.js +3 -3
- package/lib/index.js +105 -57
- package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
- package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
- package/lib/mutations/commitMutation.js +8 -8
- package/lib/mutations/validateMutation.js +4 -4
- package/lib/query/GraphQLTag.js +3 -3
- package/lib/query/fetchQuery.js +15 -3
- package/lib/store/DataChecker.js +38 -4
- package/lib/store/NormalizationEngine.js +373 -0
- package/lib/store/OperationExecutor.js +172 -113
- package/lib/store/RelayConcreteVariables.js +1 -1
- package/lib/store/RelayErrorTrie.js +2 -2
- package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
- package/lib/store/RelayModernEnvironment.js +26 -19
- package/lib/store/RelayModernRecord.js +18 -8
- package/lib/store/RelayModernSelector.js +9 -9
- package/lib/store/RelayModernStore.js +152 -43
- package/lib/store/RelayPublishQueue.js +1 -1
- package/lib/store/RelayReader.js +76 -38
- package/lib/store/RelayRecordSource.js +6 -0
- package/lib/store/RelayReferenceMarker.js +2 -1
- package/lib/store/RelayResponseNormalizer.js +88 -55
- package/lib/store/RelayStoreSubscriptions.js +34 -10
- package/lib/store/RelayStoreUtils.js +8 -1
- package/lib/store/ResolverFragments.js +2 -2
- package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
- package/lib/store/observeFragmentExperimental.js +17 -1
- package/lib/store/observeQueryExperimental.js +2 -2
- package/lib/subscription/requestSubscription.js +3 -3
- package/lib/util/RelayError.js +3 -0
- package/lib/util/RelayFeatureFlags.js +6 -2
- package/lib/util/RelayReplaySubject.js +4 -4
- package/lib/util/handlePotentialSnapshotErrors.js +2 -2
- package/lib/util/stableCopy.js +2 -2
- package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
- package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
- package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
- package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
- package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
- package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
- package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
- package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
- package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
- package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
- package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
- package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
- package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
- package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
- package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
- package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
- package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
- package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
- package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
- package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +113 -0
- package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
- package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
- package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
- package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
- package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
- package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
- package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
- package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
- package/llm-docs/api-reference/types/Disposable.mdx +4 -0
- package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
- package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
- package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
- package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
- package/llm-docs/community/learning-resources.mdx +64 -0
- package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
- package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
- package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
- package/llm-docs/debugging/relay-devtools.mdx +73 -0
- package/llm-docs/debugging/why-null.mdx +116 -0
- package/llm-docs/editor-support.mdx +55 -0
- package/llm-docs/error-reference/unknown-field.mdx +36 -0
- package/llm-docs/getting-started/babel-plugin.mdx +31 -0
- package/llm-docs/getting-started/compiler-config.mdx +25 -0
- package/llm-docs/getting-started/compiler.mdx +82 -0
- package/llm-docs/getting-started/lint-rules.mdx +87 -0
- package/llm-docs/getting-started/production.mdx +30 -0
- package/llm-docs/getting-started/quick-start.mdx +213 -0
- package/llm-docs/glossary/glossary.mdx +1040 -0
- package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
- package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
- package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
- package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
- package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
- package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
- package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
- package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
- package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
- package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
- package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
- package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
- package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
- package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
- package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
- package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
- package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
- package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
- package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
- package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
- package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
- package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
- package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
- package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
- package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
- package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
- package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
- package/llm-docs/guides/alias-directive.mdx +160 -0
- package/llm-docs/guides/catch-directive.mdx +167 -0
- package/llm-docs/guides/client-schema-extensions.mdx +208 -0
- package/llm-docs/guides/codemods.mdx +66 -0
- package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
- package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
- package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
- package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
- package/llm-docs/guides/document-comparison.mdx +106 -0
- package/llm-docs/guides/graphql-server-specification.mdx +453 -0
- package/llm-docs/guides/network-layer.mdx +69 -0
- package/llm-docs/guides/persisted-queries.mdx +328 -0
- package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
- package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
- package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
- package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
- package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
- package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
- package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
- package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
- package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
- package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
- package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
- package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
- package/llm-docs/guides/required-directive.mdx +240 -0
- package/llm-docs/guides/semantic-nullability.mdx +93 -0
- package/llm-docs/guides/testing-relay-components.mdx +642 -0
- package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
- package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
- package/llm-docs/guides/type-emission.mdx +414 -0
- package/llm-docs/home.mdx +32 -0
- package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
- package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
- package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
- package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
- package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
- package/llm-docs/principles-and-architecture/videos.mdx +50 -0
- package/llm-docs/tutorial/arrays-lists.mdx +126 -0
- package/llm-docs/tutorial/fragments-1.mdx +487 -0
- package/llm-docs/tutorial/graphql.mdx +172 -0
- package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
- package/llm-docs/tutorial/intro.mdx +58 -0
- package/llm-docs/tutorial/mutations-updates.mdx +624 -0
- package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
- package/llm-docs/tutorial/queries-1.mdx +267 -0
- package/llm-docs/tutorial/queries-2.mdx +389 -0
- package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +7 -7
- package/multi-actor-environment/ActorUtils.js.flow +1 -1
- package/multi-actor-environment/MultiActorEnvironment.js.flow +12 -8
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +3 -3
- package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
- package/mutations/RelayRecordProxy.js.flow +8 -11
- package/mutations/RelayRecordSourceMutator.js.flow +4 -4
- package/mutations/RelayRecordSourceProxy.js.flow +4 -4
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
- package/mutations/applyOptimisticMutation.js.flow +2 -2
- package/mutations/commitMutation.js.flow +20 -16
- package/mutations/createUpdatableProxy.js.flow +19 -19
- package/mutations/readUpdatableFragment.js.flow +3 -3
- package/mutations/readUpdatableQuery.js.flow +3 -3
- package/mutations/validateMutation.js.flow +7 -7
- package/network/RelayNetworkTypes.js.flow +4 -4
- package/network/RelayObservable.js.flow +16 -14
- package/network/RelayQueryResponseCache.js.flow +3 -3
- package/network/wrapNetworkWithLogObserver.js.flow +1 -1
- package/package.json +2 -1
- package/query/GraphQLTag.js.flow +22 -10
- package/query/fetchQuery.js.flow +23 -10
- package/query/fetchQuery_DEPRECATED.js.flow +1 -1
- package/store/DataChecker.js.flow +43 -9
- package/store/NormalizationEngine.js.flow +779 -0
- package/store/OperationExecutor.js.flow +173 -70
- package/store/RelayConcreteVariables.js.flow +5 -5
- package/store/RelayErrorTrie.js.flow +12 -12
- package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
- package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
- package/store/RelayModernEnvironment.js.flow +41 -26
- package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
- package/store/RelayModernOperationDescriptor.js.flow +1 -1
- package/store/RelayModernRecord.js.flow +44 -20
- package/store/RelayModernSelector.js.flow +21 -21
- package/store/RelayModernStore.js.flow +219 -58
- package/store/RelayOperationTracker.js.flow +2 -2
- package/store/RelayOptimisticRecordSource.js.flow +2 -2
- package/store/RelayPublishQueue.js.flow +21 -12
- package/store/RelayReader.js.flow +129 -57
- package/store/RelayRecordSource.js.flow +10 -0
- package/store/RelayRecordState.js.flow +1 -1
- package/store/RelayReferenceMarker.js.flow +5 -4
- package/store/RelayResponseNormalizer.js.flow +125 -57
- package/store/RelayStoreSubscriptions.js.flow +52 -8
- package/store/RelayStoreTypes.js.flow +153 -64
- package/store/RelayStoreUtils.js.flow +15 -7
- package/store/ResolverCache.js.flow +2 -2
- package/store/ResolverFragments.js.flow +12 -12
- package/store/StoreInspector.js.flow +6 -7
- package/store/cloneRelayHandleSourceField.js.flow +1 -1
- package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
- package/store/createRelayContext.js.flow +1 -1
- package/store/createRelayLoggingContext.js.flow +4 -4
- package/store/defaultGetDataID.js.flow +2 -2
- package/store/isRelayModernEnvironment.js.flow +4 -2
- package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
- package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
- package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
- package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
- package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
- package/store/observeFragmentExperimental.js.flow +49 -20
- package/store/observeQueryExperimental.js.flow +5 -5
- package/store/readInlineData.js.flow +4 -4
- package/store/waitForFragmentExperimental.js.flow +3 -3
- package/subscription/requestSubscription.js.flow +7 -7
- package/util/NormalizationNode.js.flow +34 -32
- package/util/ReaderNode.js.flow +32 -30
- package/util/RelayConcreteNode.js.flow +5 -5
- package/util/RelayError.js.flow +4 -1
- package/util/RelayFeatureFlags.js.flow +21 -1
- package/util/RelayProfiler.js.flow +1 -1
- package/util/RelayReplaySubject.js.flow +3 -3
- package/util/RelayRuntimeTypes.js.flow +11 -11
- package/util/createPayloadFor3DField.js.flow +9 -5
- package/util/deepFreeze.js.flow +2 -2
- package/util/getFragmentIdentifier.js.flow +1 -1
- package/util/getPaginationMetadata.js.flow +1 -1
- package/util/getPaginationVariables.js.flow +1 -1
- package/util/getPendingOperationsForFragment.js.flow +2 -2
- package/util/getRefetchMetadata.js.flow +6 -5
- package/util/getValueAtPath.js.flow +3 -3
- package/util/handlePotentialSnapshotErrors.js.flow +5 -5
- package/util/isEmptyObject.js.flow +1 -1
- package/util/isPromise.js.flow +2 -2
- package/util/isScalarAndEqual.js.flow +1 -1
- package/util/recycleNodesInto.js.flow +2 -2
- package/util/registerEnvironmentWithDevTools.js.flow +1 -1
- package/util/shallowFreeze.js.flow +1 -1
- package/util/stableCopy.js.flow +5 -5
- package/util/withProvidedVariables.js.flow +14 -10
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: environment
|
|
3
|
+
title: Environment
|
|
4
|
+
slug: /guided-tour/rendering/environment/
|
|
5
|
+
description: Relay guide to the environment
|
|
6
|
+
keywords:
|
|
7
|
+
- environment
|
|
8
|
+
- RelayEnvironmentProvider
|
|
9
|
+
- useRelayEnvironment
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
13
|
+
import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
|
|
14
|
+
// @fb-only
|
|
15
|
+
// @fb-only
|
|
16
|
+
|
|
17
|
+
## Relay Environment Provider
|
|
18
|
+
|
|
19
|
+
In order to render Relay components, you need to render a `RelayEnvironmentProvider` component at the root of the app:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// App root
|
|
23
|
+
|
|
24
|
+
const {RelayEnvironmentProvider} = require('react-relay');
|
|
25
|
+
const Environment = require('MyEnvironment');
|
|
26
|
+
|
|
27
|
+
function Root() {
|
|
28
|
+
return (
|
|
29
|
+
<RelayEnvironmentProvider environment={Environment}>
|
|
30
|
+
{/*... */}
|
|
31
|
+
</RelayEnvironmentProvider>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
* The `RelayEnvironmentProvider` takes an environment, which it will make available to all descendant Relay components, and which is necessary for Relay to function.
|
|
37
|
+
|
|
38
|
+
// @fb-only
|
|
39
|
+
|
|
40
|
+
## Accessing the Relay Environment
|
|
41
|
+
|
|
42
|
+
If you want to access the *current* Relay Environment within a descendant of a `RelayEnvironmentProvider` component, you can use the `useRelayEnvironment` Hook:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const {useRelayEnvironment} = require('react-relay');
|
|
46
|
+
|
|
47
|
+
function UserComponent(props: Props) {
|
|
48
|
+
const environment = useRelayEnvironment();
|
|
49
|
+
|
|
50
|
+
return (...);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// @fb-only
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
<DocsRating />
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: error-states
|
|
3
|
+
title: Error States with ErrorBoundaries
|
|
4
|
+
slug: /guided-tour/rendering/error-states/
|
|
5
|
+
description: Relay guide to rendering error states
|
|
6
|
+
keywords:
|
|
7
|
+
- rendering
|
|
8
|
+
- error
|
|
9
|
+
- boundary
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
13
|
+
import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
|
|
14
|
+
// @fb-only
|
|
15
|
+
|
|
16
|
+
// @fb-only
|
|
17
|
+
|
|
18
|
+
As you may have noticed, we mentioned that using `usePreloadedQuery` will render data from a query that was (or is) being fetched from the server, but we didn't elaborate on how to render UI to show an error if an error occurred during fetch. We will cover that in this section.
|
|
19
|
+
|
|
20
|
+
We can use [Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) components to catch errors that occur during render (due to a network error, or any kind of error), and render an alternative error UI when that occurs. The way it works is similar to how `Suspense` works, by wrapping a component tree in an error boundary, we can specify how we want to react when an error occurs, for example by rendering a fallback UI.
|
|
21
|
+
|
|
22
|
+
[Error boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) are simply components that implement the static `getDerivedStateFromError` method:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
const React = require('React');
|
|
26
|
+
|
|
27
|
+
type State = {error: ?Error};
|
|
28
|
+
|
|
29
|
+
class ErrorBoundary extends React.Component<Props, State> {
|
|
30
|
+
static getDerivedStateFromError(error): State {
|
|
31
|
+
// Set some state derived from the caught error
|
|
32
|
+
return {error: error};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
/**
|
|
39
|
+
* App.react.js
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
const ErrorBoundary = require('ErrorBoundary');
|
|
43
|
+
const React = require('React');
|
|
44
|
+
|
|
45
|
+
const MainContent = require('./MainContent.react');
|
|
46
|
+
const SecondaryContent = require('./SecondaryContent.react');
|
|
47
|
+
|
|
48
|
+
function App() {
|
|
49
|
+
return (
|
|
50
|
+
// Render an ErrorSection if an error occurs within
|
|
51
|
+
// MainContent or Secondary Content
|
|
52
|
+
<ErrorBoundary fallback={error => <ErrorUI error={error} />}>
|
|
53
|
+
<MainContent />
|
|
54
|
+
<SecondaryContent />
|
|
55
|
+
</ErrorBoundary>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
* We can use the Error Boundary to wrap subtrees and show a different UI when an error occurs within that subtree. When an error occurs, the specified `fallback` will be rendered instead of the content inside the boundary.
|
|
61
|
+
* Note that we can also control the granularity at which we render error UIs, by wrapping components at different levels with error boundaries. In this example, if any error occurs within `MainContent` or `SecondaryContent`, we will render an `ErrorSection` in place of the entire app content.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Retrying after an Error
|
|
66
|
+
|
|
67
|
+
### When using `useQueryLoader` / `loadQuery`
|
|
68
|
+
|
|
69
|
+
When using `useQueryLoader`/`loadQuery` to fetch a query, in order to retry after an error has occurred, you can call `loadQuery` again and pass the *new* query reference to `usePreloadedQuery`:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
/**
|
|
73
|
+
* ErrorBoundaryWithRetry.react.js
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
const React = require('React');
|
|
77
|
+
|
|
78
|
+
// NOTE: This is NOT actual production code;
|
|
79
|
+
// it is only used to illustrate example
|
|
80
|
+
class ErrorBoundaryWithRetry extends React.Component<Props, State> {
|
|
81
|
+
state = {error: null};
|
|
82
|
+
|
|
83
|
+
static getDerivedStateFromError(error): State {
|
|
84
|
+
return {error: error};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_retry = () => {
|
|
88
|
+
// This ends up calling loadQuery again to get and render
|
|
89
|
+
// a new query reference
|
|
90
|
+
this.props.onRetry();
|
|
91
|
+
this.setState({
|
|
92
|
+
// Clear the error
|
|
93
|
+
error: null,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
render() {
|
|
98
|
+
const {children, fallback} = this.props;
|
|
99
|
+
const {error} = this.state;
|
|
100
|
+
if (error) {
|
|
101
|
+
if (typeof fallback === 'function') {
|
|
102
|
+
return fallback({error, retry: this._retry});
|
|
103
|
+
}
|
|
104
|
+
return fallback;
|
|
105
|
+
}
|
|
106
|
+
return children;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
* When an error occurs, we render the provided `fallback`.
|
|
111
|
+
* When `retry` is called, we will clear the error, and call `loadQuery` again. This will fetch the query again and provide us a new query reference, which we can then pass down to `usePreloadedQuery`.
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
/**
|
|
115
|
+
* App.react.js
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
const ErrorBoundaryWithRetry = require('ErrorBoundaryWithRetry');
|
|
119
|
+
const React = require('React');
|
|
120
|
+
|
|
121
|
+
const MainContent = require('./MainContent.react');
|
|
122
|
+
|
|
123
|
+
const query = require('__generated__/MainContentQuery.graphql');
|
|
124
|
+
|
|
125
|
+
// NOTE: This is NOT actual production code;
|
|
126
|
+
// it is only used to illustrate example
|
|
127
|
+
function App(props) {
|
|
128
|
+
// E.g., initialQueryRef provided by router
|
|
129
|
+
const [queryRef, loadQuery] = useQueryLoader(query, props.initialQueryRef);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<ErrorBoundaryWithRetry
|
|
133
|
+
// On retry we call loadQuery again, which will update
|
|
134
|
+
// the value of queryRef from useQueryLoader with a new
|
|
135
|
+
// fresh query reference
|
|
136
|
+
onRetry={() => loadQuery(/* ... */)}
|
|
137
|
+
fallback={({error, retry}) =>
|
|
138
|
+
<>
|
|
139
|
+
<ErrorUI error={error} />
|
|
140
|
+
{/* Render a button to retry; this will attempt to re-render the
|
|
141
|
+
content inside the boundary, i.e. the query component */}
|
|
142
|
+
<Button onPress={retry}>Retry</Button>
|
|
143
|
+
</>
|
|
144
|
+
}>
|
|
145
|
+
{/* The value of queryRef will be updated after calling
|
|
146
|
+
loadQuery again */}
|
|
147
|
+
<MainContent queryRef={queryRef} />
|
|
148
|
+
</ErrorBoundaryWithRetry>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* MainContent.react.js
|
|
154
|
+
*/
|
|
155
|
+
function MainContent(props) {
|
|
156
|
+
const data = usePreloadedQuery(
|
|
157
|
+
graphql`...`,
|
|
158
|
+
props.queryRef
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return (/* ... */);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
* The sample Error Boundary in this example code will provide a `retry` function to the `fallback` which we can use to clear the error, re-load the query, and re-render with a new query ref that we can pass to the component that uses `usePreloadedQuery`. That component will consume the new query ref and suspend if necessary on the new network request.
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
### When using `useLazyLoadQuery`
|
|
168
|
+
|
|
169
|
+
When using `useLazyLoadQuery` to fetch a query, in order to retry after an error has occurred, you can attempt to re-mount *and* re-evaluate the query component by passing it a new `fetchKey`:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
/**
|
|
173
|
+
* ErrorBoundaryWithRetry.react.js
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
const React = require('React');
|
|
177
|
+
|
|
178
|
+
// NOTE: This is NOT actual production code;
|
|
179
|
+
// it is only used to illustrate example
|
|
180
|
+
class ErrorBoundaryWithRetry extends React.Component<Props, State> {
|
|
181
|
+
state = {error: null, fetchKey: 0};
|
|
182
|
+
|
|
183
|
+
static getDerivedStateFromError(error): State {
|
|
184
|
+
return {error: error, fetchKey: 0};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_retry = () => {
|
|
188
|
+
this.setState(prev => ({
|
|
189
|
+
// Clear the error
|
|
190
|
+
error: null,
|
|
191
|
+
// Increment and set a new fetchKey in order
|
|
192
|
+
// to trigger a re-evaluation and refetching
|
|
193
|
+
// of the query using useLazyLoadQuery
|
|
194
|
+
fetchKey: prev.fetchKey + 1,
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
render() {
|
|
199
|
+
const {children, fallback} = this.props;
|
|
200
|
+
const {error, fetchKey} = this.state;
|
|
201
|
+
if (error) {
|
|
202
|
+
if (typeof fallback === 'function') {
|
|
203
|
+
return fallback({error, retry: this._retry});
|
|
204
|
+
}
|
|
205
|
+
return fallback;
|
|
206
|
+
}
|
|
207
|
+
return children({fetchKey});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
* When an error occurs, we render the provided `fallback`.
|
|
212
|
+
* When `retry` is called, we will clear the error, and increment our `fetchKey` which we can then pass down to `useLazyLoadQuery`. This will make it so we re-render the component that uses `useLazyLoadQuery` with a new `fetchKey`, ensuring that the query is refetched upon the new call to `useLazyLoadQuery`.
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
/**
|
|
216
|
+
* App.react.js
|
|
217
|
+
*/
|
|
218
|
+
|
|
219
|
+
const ErrorBoundaryWithRetry = require('ErrorBoundaryWithRetry');
|
|
220
|
+
const React = require('React');
|
|
221
|
+
|
|
222
|
+
const MainContent = require('./MainContent.react');
|
|
223
|
+
|
|
224
|
+
// NOTE: This is NOT actual production code;
|
|
225
|
+
// it is only used to illustrate example
|
|
226
|
+
function App() {
|
|
227
|
+
return (
|
|
228
|
+
<ErrorBoundaryWithRetry
|
|
229
|
+
fallback={({error, retry}) =>
|
|
230
|
+
<>
|
|
231
|
+
<ErrorUI error={error} />
|
|
232
|
+
{/* Render a button to retry; this will attempt to re-render the
|
|
233
|
+
content inside the boundary, i.e. the query component */}
|
|
234
|
+
<Button onPress={retry}>Retry</Button>
|
|
235
|
+
</>
|
|
236
|
+
}>
|
|
237
|
+
{({fetchKey}) => {
|
|
238
|
+
// If we have retried, use the new `retryQueryRef` provided
|
|
239
|
+
// by the Error Boundary
|
|
240
|
+
return <MainContent fetchKey={fetchKey} />;
|
|
241
|
+
}}
|
|
242
|
+
</ErrorBoundaryWithRetry>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* MainContent.react.js
|
|
248
|
+
*/
|
|
249
|
+
function MainContent(props) {
|
|
250
|
+
const data = useLazyLoadQuery(
|
|
251
|
+
graphql`...`,
|
|
252
|
+
variables,
|
|
253
|
+
{fetchKey: props.fetchKey}
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
return (/* ... */);
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
* The sample Error Boundary in this example code will provide a `retry` function to the `fallback` which we can use to clear the error and re-render `useLazyLoadQuery` with a new `fetchKey`. This will cause the query to be re-evaluated and refetched, and `useLazyLoadQuery` start a new network request and suspend.
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
## Accessing errors in GraphQL Responses
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
<FbInternalOnly>
|
|
267
|
+
|
|
268
|
+
By default, internally at fb, Relay will *only* surface errors to React that are returned in the top-level [`errors` field](https://graphql.org/learn/validation/) if they are either:
|
|
269
|
+
|
|
270
|
+
* of `CRITICAL` severity,
|
|
271
|
+
* *or* if the top-level `data` field wasn't returned in the response.
|
|
272
|
+
|
|
273
|
+
</FbInternalOnly>
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
If you wish to access error information in your application to display user friendly messages, the recommended approach is to model and expose the error information as part of your GraphQL schema.
|
|
277
|
+
|
|
278
|
+
For example, you could expose a field in your schema that returns either the expected result, or an Error object if an error occurred while resolving that field (instead of returning null):
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
```js
|
|
282
|
+
type Error {
|
|
283
|
+
# User friendly message
|
|
284
|
+
message: String!
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
type Foo {
|
|
288
|
+
bar: Result | Error
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
<DocsRating />
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: fragments
|
|
3
|
+
title: Fragments
|
|
4
|
+
slug: /guided-tour/rendering/fragments/
|
|
5
|
+
description: Relay guide to rendering fragments
|
|
6
|
+
keywords:
|
|
7
|
+
- useFragment
|
|
8
|
+
- rendering
|
|
9
|
+
- fragment
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
13
|
+
import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
|
|
14
|
+
|
|
15
|
+
The main building block for declaring data dependencies for React Components in Relay are [GraphQL Fragments](https://graphql.org/learn/queries/#fragments). Fragments are reusable units in GraphQL that represent a set of data to query from a GraphQL type exposed in the [schema](https://graphql.org/learn/schema/).
|
|
16
|
+
|
|
17
|
+
In practice, they are a selection of fields on a GraphQL Type:
|
|
18
|
+
|
|
19
|
+
```graphql
|
|
20
|
+
fragment UserFragment on User {
|
|
21
|
+
name
|
|
22
|
+
age
|
|
23
|
+
profile_picture(scale: 2) {
|
|
24
|
+
uri
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
In order to declare a fragment inside your JavaScript code, you must use the `graphql` tag:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const {graphql} = require('react-relay');
|
|
34
|
+
|
|
35
|
+
const userFragment = graphql`
|
|
36
|
+
fragment UserFragment_user on User {
|
|
37
|
+
name
|
|
38
|
+
age
|
|
39
|
+
profile_picture(scale: 2) {
|
|
40
|
+
uri
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Rendering Fragments
|
|
47
|
+
|
|
48
|
+
In order to *render* the data for a fragment, you can use the `useFragment` Hook:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
|
|
52
|
+
|
|
53
|
+
const React = require('React');
|
|
54
|
+
|
|
55
|
+
const {graphql, useFragment} = require('react-relay');
|
|
56
|
+
|
|
57
|
+
type Props = {
|
|
58
|
+
user: UserComponent_user$key,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
function UserComponent(props: Props) {
|
|
62
|
+
const data = useFragment(
|
|
63
|
+
graphql`
|
|
64
|
+
fragment UserComponent_user on User {
|
|
65
|
+
name
|
|
66
|
+
profile_picture(scale: 2) {
|
|
67
|
+
uri
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`,
|
|
71
|
+
props.user,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<h1>{data.name}</h1>
|
|
77
|
+
<div>
|
|
78
|
+
<img src={data.profile_picture?.uri} />
|
|
79
|
+
</div>
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = UserComponent;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Let's distill what's going on here:
|
|
88
|
+
|
|
89
|
+
* `useFragment` takes a fragment definition and a *fragment reference*, and returns the corresponding `data` for that fragment and reference.
|
|
90
|
+
* This is similar to `usePreloadedQuery`, which takes a query definition and a query reference.
|
|
91
|
+
* A *fragment reference* is an object that Relay uses to *read* the data declared in the fragment definition; as you can see, the `UserComponent_user` fragment itself just declares fields on the `User` type, but we need to know *which* specific user to read those fields from; this is what the fragment reference corresponds to. In other words, a fragment reference is like *a pointer to a specific instance of a type* that we want to read data from.
|
|
92
|
+
* Note that *the component is automatically subscribed to updates to the fragment data*: if the data for this particular `User` is updated anywhere in the app (e.g. via fetching new data, or mutating existing data), the component will automatically re-render with the latest updated data.
|
|
93
|
+
* Relay will automatically generate Flow types for any declared fragments when the compiler is run, so you can use these types to declare the type for your Component's `props`.
|
|
94
|
+
* The generated Flow types include a type for the fragment reference, which is the type with the `$key` suffix: `<fragment_name>$key`, and a type for the shape of the data, which is the type with the `$data` suffix: `<fragment_name>$data`; these types are available to import from files that are generated with the following name: `<fragment_name>.graphql.js`.
|
|
95
|
+
* 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 when using `useFragment`. By using a properly typed fragment reference as input, the type of the returned `data` will automatically be Flow-typed without requiring an explicit annotation.
|
|
96
|
+
* In our example, we're typing the `user` prop as the fragment reference we need for `useFragment`, which corresponds to the `UserComponent_user$key` imported from `UserComponent_user.graphql`, which means that the type of `data` above would be: `{ name: ?string, profile_picture: ?{ uri: ?string } }`.
|
|
97
|
+
* Fragment names need to be globally unique. A useful convention to help achieve this is to name fragments with a prefix followed by an identifier: e.g. `<ComponentName>_<property_name>`. This makes it easy to identify which fragments are defined in which components and avoids name collisions when multiple fragments are defined in the same module.
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
If you need to render data from multiple fragments inside the same component, you can use `useFragment` multiple times:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
|
|
104
|
+
import type {UserComponent_viewer$key} from 'UserComponent_viewer.graphql';
|
|
105
|
+
|
|
106
|
+
const React = require('React');
|
|
107
|
+
const {graphql, useFragment} = require('react-relay');
|
|
108
|
+
|
|
109
|
+
type Props = {
|
|
110
|
+
user: UserComponent_user$key,
|
|
111
|
+
viewer: UserComponent_viewer$key,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function UserComponent(props: Props) {
|
|
115
|
+
const userData = useFragment(
|
|
116
|
+
graphql`
|
|
117
|
+
fragment UserComponent_user on User {
|
|
118
|
+
name
|
|
119
|
+
profile_picture(scale: 2) {
|
|
120
|
+
uri
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
`,
|
|
124
|
+
props.user,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const viewerData = useFragment(
|
|
128
|
+
graphql`
|
|
129
|
+
fragment UserComponent_viewer on Viewer {
|
|
130
|
+
actor {
|
|
131
|
+
name
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
`,
|
|
135
|
+
props.viewer,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<>
|
|
140
|
+
<h1>{userData.name}</h1>
|
|
141
|
+
<div>
|
|
142
|
+
<img src={userData.profile_picture?.uri} />
|
|
143
|
+
Acting as: {viewerData.actor?.name ?? 'Unknown'}
|
|
144
|
+
</div>
|
|
145
|
+
</>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = UserComponent;
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Composing Fragments
|
|
153
|
+
|
|
154
|
+
In GraphQL, fragments are reusable units, which means they can include *other* fragments, and consequently a fragment can be included within other fragments or [queries](../queries/):
|
|
155
|
+
|
|
156
|
+
```graphql
|
|
157
|
+
fragment UserFragment on User {
|
|
158
|
+
name
|
|
159
|
+
age
|
|
160
|
+
profile_picture(scale: 2) {
|
|
161
|
+
uri
|
|
162
|
+
}
|
|
163
|
+
...AnotherUserFragment
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
fragment AnotherUserFragment on User {
|
|
167
|
+
username
|
|
168
|
+
...FooUserFragment
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
With Relay, you can compose fragment components in a similar way, using both component composition and fragment composition. Each React component is responsible for fetching the data dependencies of its direct children - just as it has to know about its children's props in order to render them correctly. This pattern means that developers are able to reason locally about components - what data they need, what components they render - but Relay is able to derive a global view of the data dependencies of an entire UI tree.
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
/**
|
|
177
|
+
* UsernameSection.react.js
|
|
178
|
+
*
|
|
179
|
+
* Child Fragment Component
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
import type {UsernameSection_user$key} from 'UsernameSection_user.graphql';
|
|
183
|
+
|
|
184
|
+
const React = require('React');
|
|
185
|
+
const {graphql, useFragment} = require('react-relay');
|
|
186
|
+
|
|
187
|
+
type Props = {
|
|
188
|
+
user: UsernameSection_user$key,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
function UsernameSection(props: Props) {
|
|
192
|
+
const data = useFragment(
|
|
193
|
+
graphql`
|
|
194
|
+
fragment UsernameSection_user on User {
|
|
195
|
+
username
|
|
196
|
+
}
|
|
197
|
+
`,
|
|
198
|
+
props.user,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return <div>{data.username ?? 'Unknown'}</div>;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = UsernameSection;
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
/**
|
|
209
|
+
* UserComponent.react.js
|
|
210
|
+
*
|
|
211
|
+
* Parent Fragment Component
|
|
212
|
+
*/
|
|
213
|
+
|
|
214
|
+
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
|
|
215
|
+
|
|
216
|
+
const React = require('React');
|
|
217
|
+
const {graphql, useFragment} = require('react-relay');
|
|
218
|
+
|
|
219
|
+
const UsernameSection = require('./UsernameSection.react');
|
|
220
|
+
|
|
221
|
+
type Props = {
|
|
222
|
+
user: UserComponent_user$key,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
function UserComponent(props: Props) {
|
|
226
|
+
const user = useFragment(
|
|
227
|
+
graphql`
|
|
228
|
+
fragment UserComponent_user on User {
|
|
229
|
+
name
|
|
230
|
+
age
|
|
231
|
+
profile_picture(scale: 2) {
|
|
232
|
+
uri
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
# Include child fragment:
|
|
236
|
+
...UsernameSection_user
|
|
237
|
+
}
|
|
238
|
+
`,
|
|
239
|
+
props.user,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<>
|
|
244
|
+
<h1>{user.name}</h1>
|
|
245
|
+
<div>
|
|
246
|
+
<img src={user.profile_picture?.uri} />
|
|
247
|
+
{user.age}
|
|
248
|
+
|
|
249
|
+
{/* Render child component, passing the _fragment reference_: */}
|
|
250
|
+
<UsernameSection user={user} />
|
|
251
|
+
</div>
|
|
252
|
+
</>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
module.exports = UserComponent;
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
There are a few things to note here:
|
|
260
|
+
|
|
261
|
+
* `UserComponent` both renders `UsernameSection`, *and* includes the fragment declared by `UsernameSection` inside its own `graphql` fragment declaration.
|
|
262
|
+
* `UsernameSection` expects a *fragment reference* as the `user` prop. As we've mentioned before, a fragment reference is an object that Relay uses to *read* the data declared in the fragment definition; as you can see, the child `UsernameSection_user` fragment itself just declares fields on the `User` type, but we need to know *which* specific user to read those fields from; this is what the fragment reference corresponds to. In other words, a fragment reference is like *a pointer to a specific instance of a type* that we want to read data from.
|
|
263
|
+
* Note that in this case the `user` passed to `UsernameSection`, i.e. the fragment reference, *doesn't actually contain any of the data declared by the child `UsernameSection` component*; instead, `UsernameSection` will use the fragment reference to read the data *it* declared internally, using `useFragment`.
|
|
264
|
+
* This means that the parent component will not receive the data selected by a child component (unless that parent explicitly selected the same fields). Likewise, child components will not receive the data selected by their parents (again, unless the child selected those same fields).
|
|
265
|
+
* This prevents separate components from *even accidentally* having implicit dependencies on each other. If this wasn't the case, modifying a component could break other components!
|
|
266
|
+
* This allows us to reason locally about our components and modify them without worrying about affecting other components.
|
|
267
|
+
* This is known as [*data masking*](../../../principles-and-architecture/thinking-in-relay/).
|
|
268
|
+
* The *fragment reference* that the child (i.e. `UsernameSection`) expects is the result of reading a parent fragment that *includes* the child fragment. In our particular example, that means the result of reading a fragment that includes `...UsernameSection_user` will be the fragment reference that `UsernameSection` expects. In other words, the data obtained as a result of reading a fragment via `useFragment` also serves as the fragment reference for any child fragments included in that fragment.
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
## Composing Fragments into Queries
|
|
272
|
+
|
|
273
|
+
Fragments in Relay allow declaring data dependencies for a component, but they ***can't be fetched by themselves***. Instead, they need to be included in a query, either directly or transitively. This means that *all fragments must belong to a query when they are rendered*, or in other words, they must be "rooted" under some query. Note that a single fragment can still be included by multiple queries, but when rendering a specific *instance* of a fragment component, it must have been included as part of a specific query request.
|
|
274
|
+
|
|
275
|
+
To fetch and render a query that includes a fragment, you can compose them in the same way fragments are composed, as shown in the [Composing Fragments](#composing-fragments) section:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
/**
|
|
279
|
+
* UserComponent.react.js
|
|
280
|
+
*
|
|
281
|
+
* Fragment Component
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
import type {UserComponent_user$key} from 'UserComponent_user.graphql';
|
|
285
|
+
|
|
286
|
+
const React = require('React');
|
|
287
|
+
const {graphql, useFragment} = require('react-relay');
|
|
288
|
+
|
|
289
|
+
type Props = {
|
|
290
|
+
user: UserComponent_user$key,
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
function UserComponent(props: Props) {
|
|
294
|
+
const data = useFragment(
|
|
295
|
+
graphql`...`,
|
|
296
|
+
props.user,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
return (...);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
module.exports = UserComponent;
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
```js
|
|
306
|
+
/**
|
|
307
|
+
* App.react.js
|
|
308
|
+
*
|
|
309
|
+
* Query Component
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
import type {AppQuery} from 'AppQuery.graphql';
|
|
313
|
+
import type {PreloadedQuery} from 'react-relay';
|
|
314
|
+
|
|
315
|
+
const React = require('React');
|
|
316
|
+
const {graphql, usePreloadedQuery} = require('react-relay');
|
|
317
|
+
|
|
318
|
+
const UserComponent = require('./UserComponent.react');
|
|
319
|
+
|
|
320
|
+
type Props = {
|
|
321
|
+
appQueryRef: PreloadedQuery<AppQuery>,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function App({appQueryRef}) {
|
|
325
|
+
const data = usePreloadedQuery(
|
|
326
|
+
graphql`
|
|
327
|
+
query AppQuery($id: ID!) {
|
|
328
|
+
user(id: $id) {
|
|
329
|
+
name
|
|
330
|
+
|
|
331
|
+
# Include child fragment:
|
|
332
|
+
...UserComponent_user
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
`,
|
|
336
|
+
appQueryRef,
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<>
|
|
341
|
+
<h1>{data.user?.name}</h1>
|
|
342
|
+
{/* Render child component, passing the fragment reference: */}
|
|
343
|
+
<UserComponent user={data.user} />
|
|
344
|
+
</>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Note that:
|
|
350
|
+
|
|
351
|
+
* The *fragment reference* that `UserComponent` expects is the result of reading a parent query that includes its fragment, which in our case means a query that includes `...UsernameSection_user`. In other words, the `data` obtained as a result of `usePreloadedQuery` also serves as the fragment reference for any child fragments included in that query.
|
|
352
|
+
* As mentioned previously, *all fragments must belong to a query when they are rendered,* which means that all fragment components *must* be descendants of a query. This guarantees that you will always be able to provide a fragment reference for `useFragment`, by starting from the result of reading a root query with `usePreloadedQuery`.
|
|
353
|
+
|
|
354
|
+
<DocsRating />
|