relay-runtime 20.1.0 → 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.
Files changed (262) hide show
  1. package/experimental.js +1 -1
  2. package/experimental.js.flow +8 -8
  3. package/handlers/connection/ConnectionHandler.js.flow +5 -5
  4. package/handlers/connection/ConnectionInterface.js.flow +1 -1
  5. package/index.js +1 -1
  6. package/index.js.flow +125 -62
  7. package/lib/experimental.js +3 -3
  8. package/lib/index.js +105 -57
  9. package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
  10. package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
  11. package/lib/mutations/commitMutation.js +8 -8
  12. package/lib/mutations/validateMutation.js +4 -4
  13. package/lib/query/GraphQLTag.js +3 -3
  14. package/lib/query/fetchQuery.js +15 -3
  15. package/lib/store/DataChecker.js +38 -4
  16. package/lib/store/NormalizationEngine.js +373 -0
  17. package/lib/store/OperationExecutor.js +172 -113
  18. package/lib/store/RelayConcreteVariables.js +1 -1
  19. package/lib/store/RelayErrorTrie.js +2 -2
  20. package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
  21. package/lib/store/RelayModernEnvironment.js +26 -19
  22. package/lib/store/RelayModernRecord.js +18 -8
  23. package/lib/store/RelayModernSelector.js +9 -9
  24. package/lib/store/RelayModernStore.js +152 -43
  25. package/lib/store/RelayPublishQueue.js +1 -1
  26. package/lib/store/RelayReader.js +76 -38
  27. package/lib/store/RelayRecordSource.js +6 -0
  28. package/lib/store/RelayReferenceMarker.js +2 -1
  29. package/lib/store/RelayResponseNormalizer.js +88 -55
  30. package/lib/store/RelayStoreSubscriptions.js +34 -10
  31. package/lib/store/RelayStoreUtils.js +8 -1
  32. package/lib/store/ResolverFragments.js +2 -2
  33. package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
  34. package/lib/store/observeFragmentExperimental.js +17 -1
  35. package/lib/store/observeQueryExperimental.js +2 -2
  36. package/lib/subscription/requestSubscription.js +3 -3
  37. package/lib/util/RelayError.js +3 -0
  38. package/lib/util/RelayFeatureFlags.js +6 -2
  39. package/lib/util/RelayReplaySubject.js +4 -4
  40. package/lib/util/handlePotentialSnapshotErrors.js +2 -2
  41. package/lib/util/stableCopy.js +2 -2
  42. package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
  43. package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
  44. package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
  45. package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
  46. package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
  47. package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
  48. package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
  49. package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
  50. package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
  51. package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
  52. package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
  53. package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
  54. package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
  55. package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
  56. package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
  57. package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
  58. package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
  59. package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
  60. package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
  61. package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
  62. package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
  63. package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +113 -0
  64. package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
  65. package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
  66. package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
  67. package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
  68. package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
  69. package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
  70. package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
  71. package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
  72. package/llm-docs/api-reference/types/Disposable.mdx +4 -0
  73. package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
  74. package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
  75. package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
  76. package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
  77. package/llm-docs/community/learning-resources.mdx +64 -0
  78. package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
  79. package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
  80. package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
  81. package/llm-docs/debugging/relay-devtools.mdx +73 -0
  82. package/llm-docs/debugging/why-null.mdx +116 -0
  83. package/llm-docs/editor-support.mdx +55 -0
  84. package/llm-docs/error-reference/unknown-field.mdx +36 -0
  85. package/llm-docs/getting-started/babel-plugin.mdx +31 -0
  86. package/llm-docs/getting-started/compiler-config.mdx +25 -0
  87. package/llm-docs/getting-started/compiler.mdx +82 -0
  88. package/llm-docs/getting-started/lint-rules.mdx +87 -0
  89. package/llm-docs/getting-started/production.mdx +30 -0
  90. package/llm-docs/getting-started/quick-start.mdx +213 -0
  91. package/llm-docs/glossary/glossary.mdx +1040 -0
  92. package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
  93. package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
  94. package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
  95. package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
  96. package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
  97. package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
  98. package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
  99. package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
  100. package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
  101. package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
  102. package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
  103. package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
  104. package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
  105. package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
  106. package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
  107. package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
  108. package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
  109. package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
  110. package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
  111. package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
  112. package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
  113. package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
  114. package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
  115. package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
  116. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
  117. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
  118. package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
  119. package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
  120. package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
  121. package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
  122. package/llm-docs/guides/alias-directive.mdx +160 -0
  123. package/llm-docs/guides/catch-directive.mdx +167 -0
  124. package/llm-docs/guides/client-schema-extensions.mdx +208 -0
  125. package/llm-docs/guides/codemods.mdx +66 -0
  126. package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
  127. package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
  128. package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
  129. package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
  130. package/llm-docs/guides/document-comparison.mdx +106 -0
  131. package/llm-docs/guides/graphql-server-specification.mdx +453 -0
  132. package/llm-docs/guides/network-layer.mdx +69 -0
  133. package/llm-docs/guides/persisted-queries.mdx +328 -0
  134. package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
  135. package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
  136. package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
  137. package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
  138. package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
  139. package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
  140. package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
  141. package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
  142. package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
  143. package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
  144. package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
  145. package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
  146. package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
  147. package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
  148. package/llm-docs/guides/required-directive.mdx +240 -0
  149. package/llm-docs/guides/semantic-nullability.mdx +93 -0
  150. package/llm-docs/guides/testing-relay-components.mdx +642 -0
  151. package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
  152. package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
  153. package/llm-docs/guides/type-emission.mdx +414 -0
  154. package/llm-docs/home.mdx +32 -0
  155. package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
  156. package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
  157. package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
  158. package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
  159. package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
  160. package/llm-docs/principles-and-architecture/videos.mdx +50 -0
  161. package/llm-docs/tutorial/arrays-lists.mdx +126 -0
  162. package/llm-docs/tutorial/fragments-1.mdx +487 -0
  163. package/llm-docs/tutorial/graphql.mdx +172 -0
  164. package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
  165. package/llm-docs/tutorial/intro.mdx +58 -0
  166. package/llm-docs/tutorial/mutations-updates.mdx +624 -0
  167. package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
  168. package/llm-docs/tutorial/queries-1.mdx +267 -0
  169. package/llm-docs/tutorial/queries-2.mdx +389 -0
  170. package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
  171. package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
  172. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +7 -7
  173. package/multi-actor-environment/ActorUtils.js.flow +1 -1
  174. package/multi-actor-environment/MultiActorEnvironment.js.flow +12 -8
  175. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +3 -3
  176. package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
  177. package/mutations/RelayRecordProxy.js.flow +8 -11
  178. package/mutations/RelayRecordSourceMutator.js.flow +4 -4
  179. package/mutations/RelayRecordSourceProxy.js.flow +4 -4
  180. package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
  181. package/mutations/applyOptimisticMutation.js.flow +2 -2
  182. package/mutations/commitMutation.js.flow +20 -16
  183. package/mutations/createUpdatableProxy.js.flow +19 -19
  184. package/mutations/readUpdatableFragment.js.flow +3 -3
  185. package/mutations/readUpdatableQuery.js.flow +3 -3
  186. package/mutations/validateMutation.js.flow +7 -7
  187. package/network/RelayNetworkTypes.js.flow +4 -4
  188. package/network/RelayObservable.js.flow +16 -14
  189. package/network/RelayQueryResponseCache.js.flow +3 -3
  190. package/network/wrapNetworkWithLogObserver.js.flow +1 -1
  191. package/package.json +2 -1
  192. package/query/GraphQLTag.js.flow +22 -10
  193. package/query/fetchQuery.js.flow +23 -10
  194. package/query/fetchQuery_DEPRECATED.js.flow +1 -1
  195. package/store/DataChecker.js.flow +43 -9
  196. package/store/NormalizationEngine.js.flow +779 -0
  197. package/store/OperationExecutor.js.flow +173 -70
  198. package/store/RelayConcreteVariables.js.flow +5 -5
  199. package/store/RelayErrorTrie.js.flow +12 -12
  200. package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
  201. package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
  202. package/store/RelayModernEnvironment.js.flow +41 -26
  203. package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
  204. package/store/RelayModernOperationDescriptor.js.flow +1 -1
  205. package/store/RelayModernRecord.js.flow +46 -22
  206. package/store/RelayModernSelector.js.flow +21 -21
  207. package/store/RelayModernStore.js.flow +219 -58
  208. package/store/RelayOperationTracker.js.flow +2 -2
  209. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  210. package/store/RelayPublishQueue.js.flow +21 -12
  211. package/store/RelayReader.js.flow +130 -58
  212. package/store/RelayRecordSource.js.flow +10 -0
  213. package/store/RelayRecordState.js.flow +1 -1
  214. package/store/RelayReferenceMarker.js.flow +5 -4
  215. package/store/RelayResponseNormalizer.js.flow +130 -54
  216. package/store/RelayStoreSubscriptions.js.flow +52 -8
  217. package/store/RelayStoreTypes.js.flow +153 -64
  218. package/store/RelayStoreUtils.js.flow +15 -7
  219. package/store/ResolverCache.js.flow +2 -2
  220. package/store/ResolverFragments.js.flow +12 -12
  221. package/store/StoreInspector.js.flow +6 -7
  222. package/store/cloneRelayHandleSourceField.js.flow +1 -1
  223. package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
  224. package/store/createRelayContext.js.flow +1 -1
  225. package/store/createRelayLoggingContext.js.flow +4 -4
  226. package/store/defaultGetDataID.js.flow +2 -2
  227. package/store/isRelayModernEnvironment.js.flow +4 -2
  228. package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
  229. package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
  230. package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
  231. package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
  232. package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
  233. package/store/observeFragmentExperimental.js.flow +49 -20
  234. package/store/observeQueryExperimental.js.flow +5 -5
  235. package/store/readInlineData.js.flow +4 -4
  236. package/store/waitForFragmentExperimental.js.flow +3 -3
  237. package/subscription/requestSubscription.js.flow +7 -7
  238. package/util/NormalizationNode.js.flow +34 -32
  239. package/util/ReaderNode.js.flow +32 -30
  240. package/util/RelayConcreteNode.js.flow +5 -5
  241. package/util/RelayError.js.flow +4 -1
  242. package/util/RelayFeatureFlags.js.flow +21 -1
  243. package/util/RelayProfiler.js.flow +1 -1
  244. package/util/RelayReplaySubject.js.flow +3 -3
  245. package/util/RelayRuntimeTypes.js.flow +11 -11
  246. package/util/createPayloadFor3DField.js.flow +9 -5
  247. package/util/deepFreeze.js.flow +2 -2
  248. package/util/getFragmentIdentifier.js.flow +1 -1
  249. package/util/getPaginationMetadata.js.flow +1 -1
  250. package/util/getPaginationVariables.js.flow +1 -1
  251. package/util/getPendingOperationsForFragment.js.flow +2 -2
  252. package/util/getRefetchMetadata.js.flow +6 -5
  253. package/util/getValueAtPath.js.flow +3 -3
  254. package/util/handlePotentialSnapshotErrors.js.flow +5 -5
  255. package/util/isEmptyObject.js.flow +1 -1
  256. package/util/isPromise.js.flow +2 -2
  257. package/util/isScalarAndEqual.js.flow +1 -1
  258. package/util/recycleNodesInto.js.flow +2 -2
  259. package/util/registerEnvironmentWithDevTools.js.flow +1 -1
  260. package/util/shallowFreeze.js.flow +1 -1
  261. package/util/stableCopy.js.flow +5 -5
  262. 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 />