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.
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 +44 -20
  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 +129 -57
  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 +125 -57
  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,267 @@
1
+ # Query Basics
2
+
3
+ In this section:
4
+
5
+ * We’ll take a React component that displays hard-coded placeholder data and modify it so that it fetches its data using a GraphQL query.
6
+ * We’ll learn how to use the TypeScript types that Relay generates from your GraphQL to ensure type safety.
7
+
8
+ * * *
9
+ With Relay, you fetch data using GraphQL Queries. A Query specifies a part of the GraphQL graph for your app to fetch, starting from some root node and traversing from node to node to retrieve a particular set of data in the shape of a tree.
10
+
11
+ ![A query selects a particular subgraph](/img/docs/tutorial/query-upon-graph.png)
12
+
13
+ Right now, our example app doesn’t fetch any data, it just renders some placeholder data that’s hard-coded into the React components. Let’s modify it to fetch some data using Relay.
14
+
15
+ Open up the file called `Newsfeed.tsx`. (All of the components in the tutorial are in `src/components`.) In it you should see a `<Newsfeed>` component where the data is hard-coded:
16
+
17
+ ```jsx
18
+ export default function Newsfeed() {
19
+ const story = {
20
+ title: "Placeholder Story",
21
+ summary:
22
+ "Placeholder data, to be replaced with data fetched via GraphQL",
23
+ poster: {
24
+ name: "Placeholder Person",
25
+ profilePicture: {
26
+ url: "/assets/cat_avatar.png",
27
+ },
28
+ },
29
+ thumbnail: {
30
+ url: "/assets/placeholder.jpeg",
31
+ },
32
+ };
33
+ return (
34
+ <div className="newsfeed">
35
+ <Story story={story} />
36
+ </div>
37
+ );
38
+ }
39
+ ```
40
+
41
+ We’re going to replace this placeholder data with data fetched from the server. First we need to define a GraphQL query. Add the following declaration above the Newsfeed component:
42
+
43
+ ```
44
+ import { graphql } from 'relay-runtime';
45
+
46
+ // color1
47
+ const NewsfeedQuery = graphql`
48
+ // color2
49
+ query NewsfeedQuery {
50
+ topStory {
51
+ // color3
52
+ title
53
+ // color3
54
+ summary
55
+ // color4
56
+ poster {
57
+ name
58
+ profilePicture {
59
+ url
60
+ }
61
+ }
62
+ thumbnail {
63
+ url
64
+ }
65
+ }
66
+ }
67
+ `;
68
+ ```
69
+
70
+ Let’s break this down:
71
+
72
+ * To embed GraphQL within JavaScript, we put a string literal <span className="color1">marked with the <code>graphql``</code> tag</span>. This tag allows the Relay compiler to find and compile the GraphQL within a JavaScript codebase.
73
+ * Our GraphQL string consists of a <span className="color2">query declaration</span> with the keyword `query` and then a query name. The query name must be globally unique and end with `Query`.
74
+ * Inside the query declaration are *fields*, which specify what information to query for*:*
75
+ * Some fields are *<span className="color3">scalar fields</span>* that retrieve a string, number, or other unit of information.
76
+ * Other fields are *<span className="color4">edges</span>* that let us traverse from one node in the graph to another. When a field is an edge, it’s followed by another block `{ }` containing fields for the node at the other end of the edge. Here, the `poster` field is an edge that goes from a Story to a Person who posted it. Once we’ve traversed to the Person, we can include fields about the Person such as their `name`.
77
+
78
+ This illustrates the part of the graph that this query is asking for:
79
+
80
+ ![Parts of the GraphQL query](/img/docs/tutorial/query-breakdown.png)
81
+
82
+ Now that we’ve defined the query, we need to do two things.
83
+ 1. Run the Relay compiler so that it knows about the new Graphql query. (`npm run relay`)
84
+ 2. Modify our React component to fetch it and to use the data returned by the server.
85
+
86
+ If you open `package.json` you will find the script `relay` is hooked up to run the Relay compiler. This is what `npm run relay` does. Once the compiler successfully updates/generated the new compiled query you will be able to find it in the `__generated__` folder under `src/components/` as `NewsfeedQuery.graphql.ts`.
87
+
88
+ Next, turn back to the `Newsfeed` component and start by deleting the placeholder data. Then, replace it with this:
89
+
90
+ ```
91
+ import { useLazyLoadQuery } from "react-relay";
92
+
93
+ export default function Newsfeed({}) {
94
+ const data = useLazyLoadQuery(
95
+ // color1
96
+ NewsfeedQuery,
97
+ // color2
98
+ {},
99
+ );
100
+ const story = data.topStory;
101
+ // As before:
102
+ return (
103
+ <div className="newsfeed">
104
+ <Story story={story} />
105
+ </div>
106
+ );
107
+ }
108
+ ```
109
+
110
+ The `useLazyLoadQuery` hook fetches and returns the data. It takes two arguments:
111
+
112
+ * The <span className="color1">GraphQL query</span> that we defined before.
113
+ * <span className="color2">Variables</span> that are passed to the server with the query. This query doesn’t declare any variables, so it’s an empty object.
114
+
115
+ The object that `useLazyLoadQuery` returns has the same shape as the query. For instance, if printed in JSON format it might look like this:
116
+
117
+ ```
118
+ {
119
+ topStory: {
120
+ title: "Local Yak Named Yak of the Year",
121
+ summary: "The annual Yak of the Year awards ceremony ...",
122
+ poster: {
123
+ name: "Baller Bovine Board",
124
+ profilePicture: {
125
+ url: '/images/baller_bovine_board.jpg',
126
+ },
127
+ },
128
+ thumbnail: {
129
+ url: '/images/max_the_yak.jpg',
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ Notice that each field selected by the GraphQL query corresponds to a property in the JSON response.
136
+
137
+ At this point, you should see a story fetched from the server:
138
+
139
+ ![Screenshot](/img/docs/tutorial/queries-basic-screenshot.png)
140
+
141
+ :::note
142
+ The server's responses are artificially slowed down to make loading states perceptible, which will come in handy when we add more interactivity to the app. If you want to remove the delay, open `server/index.js` and remove the call to `sleep()`.
143
+ :::
144
+
145
+ The `useLazyLoadQuery` hook fetches the data when the component is first rendered. Relay also has APIs for pre-fetching the data before your app has even loaded — these are covered later. In any case, Relay uses Suspense to show a loading indicator until the data is available.
146
+
147
+ This is Relay in its most basic form: fetching the results of a GraphQL query when a component is rendered. As the tutorial progresses, we’ll see how Relay’s features fit together to make your app more maintainable — starting with a look at how Relay generates TypeScript types corresponding to each query.
148
+
149
+ <details>
150
+ <summary>Deep dive: Suspense for Data Loading</summary>
151
+
152
+ *Suspense* is a new API in React that lets React wait while data is loaded before it renders components that need that data. When a component needs to load data before rendering, React shows a loading indicator. You control the loading indicator's location and style using a special component called `Suspense`.
153
+
154
+ Right now, there's a `Suspense` component inside `App.tsx`, which is what shows the spinner while `useLazyLoadQuery` is loading data.
155
+
156
+ We'll look at Suspense in more detail in later sections when we add some more interactivity to the app.
157
+ </details>
158
+
159
+ <details>
160
+ <summary>Deep dive: Queries are Static</summary>
161
+
162
+ All of the GraphQL strings in a Relay app are pre-processed by the Relay compiler and removed from the resulting bundled code. This means you can’t construct GraphQL queries at runtime — they have to be static string literals so that they’re known at compile time. But it comes with major advantages.
163
+
164
+ First, it allows Relay to generate type definitions for the results of the query, making your code more type-safe.
165
+
166
+ Second, Relay replaces the GraphQL string literal with an object that tells Relay what to do. This is much faster than using the GraphQL strings directly at runtime.
167
+
168
+ Also, Relay’s compiler can be configured to [save queries to the server](/docs/guides/persisted-queries/) when you build your app, so that at runtime the client need only send a query ID instead of the query itself. This saves bundle size and network bandwidth, and can prevent attackers from writing malicious queries since only those your app was built with need be available.
169
+
170
+ So when you have a GraphQL tagged string literal in your program...
171
+
172
+ ```
173
+ const MyQuery = graphql`
174
+ query MyQuery {
175
+ viewer {
176
+ name
177
+ }
178
+ }
179
+ `;
180
+ ```
181
+
182
+ ... the JavaScript variable `MyQuery` is actually assigned to an object that looks something like this:
183
+
184
+ ```
185
+ const MyQuery = {
186
+ kind: "query",
187
+ selections: [
188
+ {
189
+ name: "viewer",
190
+ kind: "LinkedField",
191
+ selections: [
192
+ name: "name",
193
+ kind: "ScalarField",
194
+ ],
195
+ }
196
+ ]
197
+ };
198
+ ```
199
+
200
+ along with various other properties and information. These data structures are carefully designed to allow the JIT to run Relay’s payload processing code very quickly. If you’re curious, you can use the [Relay Compiler Explorer](/compiler-explorer) to play with it.
201
+
202
+ </details>
203
+
204
+ * * *
205
+
206
+ ## Relay and the Type System
207
+
208
+ You might notice that TypeScript reports an error with this code as we’ve written it:
209
+
210
+ ```
211
+ const story = data.topStory;
212
+ ^^^^^^^^
213
+ Property 'topStory' does not exist on type 'unknown'
214
+ ```
215
+
216
+ To fix this, we need to annotate the call to `useLazyLoadQuery` with types that Relay generates. That way, TypeScript will know what type `data` should have based on the fields we’ve selected in our query. Add the following:
217
+
218
+ ```
219
+ // change-line
220
+ import type {NewsfeedQuery as NewsfeedQueryType} from './__generated__/NewsfeedQuery.graphql';
221
+
222
+ function Newsfeed({}) {
223
+ const data = useLazyLoadQuery
224
+ // change-line
225
+ <NewsfeedQueryType>
226
+ (NewsfeedQuery, {});
227
+ ...
228
+ }
229
+ ```
230
+
231
+ If we look inside `__generated__/NewsfeedQuery.graphql` we’ll see the following type definition — with the annotation we’ve just added, TypeScript knows that `data` should have this type:
232
+
233
+ ```
234
+ export type NewsfeedQuery$data = {
235
+ readonly topStory: {
236
+ readonly poster: {
237
+ readonly name: string | null;
238
+ readonly profilePicture: {
239
+ readonly url: string;
240
+ } | null;
241
+ };
242
+ readonly summary: string | null;
243
+ readonly thumbnail: {
244
+ readonly url: string;
245
+ } | null;
246
+ readonly title: string;
247
+ } | null;
248
+ };
249
+ ```
250
+
251
+ The Relay compiler generates TypeScript types corresponding to every piece of GraphQL that you have in your app within a <code>graphql``</code> literal. As long as <code>npm run dev</code> is running, the Relay compiler will automatically regenerate these files whenever you save one of your JavaScript source files, so you don’t need to refresh anything to keep them up to date.
252
+
253
+ Using Relay’s generated types makes your app safer and more maintainable. In addition to TypeScript, Relay supports the Flow type system if you want to use that instead. When using Flow, the extra annotation on `useLazyLoadQuery` is not needed, because Flow directly understands the contents of the <code>graphql``</code> tagged literal.
254
+
255
+ We’ll revisit types throughout this tutorial. But next, we'll look at an even more important way that Relay helps us with maintainability.
256
+
257
+ * * *
258
+
259
+ ## Summary
260
+
261
+ Queries are the foundation of fetching GraphQL data. We’ve seen:
262
+
263
+ * How to define a GraphQL query within our app using the <code>graphql``</code> tagged literal.
264
+ * How to use the `useLazyLoadQuery` hook to fetch the results of a query when a component renders.
265
+ * How to import Relay's generated types for type safety.
266
+
267
+ In the next section, we’ll look at Fragments, one of the most core and distinctive aspects of Relay. Fragments let each individual component define its own data requirements, while retaining the performance advantages of issuing a single query to the server.
@@ -0,0 +1,389 @@
1
+ # Queries for Interactions
2
+
3
+ We’ve seen how fragments let us specify data requirements in each component, yet at runtime perform only a single query for an entire screen. Here we’ll look at a situation where we *want* a second query on the same screen. This will also let us explore some more features of GraphQL queries.
4
+
5
+ * We’ll build a hovercard that shows more details about the poster of a story when you hover over their name.
6
+ * The hovercard will use a second query to fetch additional information that’s only needed if the user hovers.
7
+ * We’ll use **query variables** to tell the server which person we’d like more details about.
8
+ * We’ll see how to improve performance with **preloaded queries**.
9
+
10
+ After covering these topics, we’ll return to look at some more advanced features of Fragments.
11
+
12
+ * * *
13
+
14
+ In this section we’ll add a hovercard to `PosterByline` so that you can see more details about the poster of a story by hovering over their name.
15
+
16
+ <details>
17
+ <summary>Deep dive: When to use a secondary query</summary>
18
+
19
+ We've mentioned before that Relay is designed to help you fetch all of your data requirements for an entire screen up-front. But we can generalize this and say that it's a *user interaction* that should have at most one query. Navigating to another screen is just one common type of user interaction.
20
+
21
+ Within a screen, some interactions may disclose additional data from what was shown initially. If an interaction is performed relatively rarely, but needs a significant amount of additional data, it can be smart to fetch that additional data in a second query, performed when the interaction happens, rather than up-front when the screen is first loaded. This makes that initial load faster and less expensive.
22
+
23
+ There are also some interactions where the amount of data fetched is indefinite — e.g., a hovercard within a hovercard — and not feasible to know statically.
24
+
25
+ If data is lower-priority and should be loaded after the main data has loaded, but should pop in automatically without further user input, Relay has a feature called a *deferred fragment* for that. We'll cover it later.
26
+ </details>
27
+
28
+ We’ve already prepared a hovercard component that you can put to use. However, it has been in a directory called `future` in order to avoid compilation errors since it uses `ImageFragment`. Now that we’re at this stage of the tutorial, you can move the modules in `future` into `src/components`:
29
+
30
+ ```shell
31
+ mv future/* src/components
32
+ ```
33
+
34
+ Now, if you did the exercise to make `PosterByline` use fragments, the `PosterByline` component should look something like this:
35
+
36
+ ```
37
+ export default function PosterByline({ poster }: Props): React.ReactElement {
38
+ const data = useFragment(PosterBylineFragment, poster);
39
+ return (
40
+ <div className="byline">
41
+ <Image image={data.profilePicture} width={60} height={60} className="byline__image" />
42
+ <div className="byline__name">{data.name}</div>
43
+ </div>
44
+ );
45
+ }
46
+ ```
47
+
48
+ To use the hovercard component, you can make the following changes:
49
+
50
+ ```
51
+ // change-line
52
+ import Hovercard from './Hovercard';
53
+ // change-line
54
+ import PosterDetailsHovercardContents from './PosterDetailsHovercardContents';
55
+ // change-line
56
+ const {useRef} = React;
57
+
58
+ ...
59
+
60
+ export default function PosterByline({ poster }: Props): React.ReactElement {
61
+ const data = useFragment(PosterBylineFragment, poster);
62
+ // change-line
63
+ const hoverRef = useRef(null);
64
+ return (
65
+ <div
66
+ // change-line
67
+ ref={hoverRef}
68
+ className="byline">
69
+ <Image image={data.profilePicture} width={60} height={60} className="byline__image" />
70
+ <div className="byline__name">{data.name}</div>
71
+ // change
72
+ <Hovercard targetRef={hoverRef}>
73
+ <PosterDetailsHovercardContents />
74
+ </Hovercard>
75
+ // end-change
76
+ </div>
77
+ );
78
+ }
79
+ ```
80
+
81
+ You should now see that whenever you hover over someone’s name, you get a hovercard with more information. If you look inside `PosterDetailsHovercardContents.tsx`, you’ll find that it performs a second query with `useLazyLoadQuery` to fetch additional information when that component is mounted.
82
+
83
+ There’s just one problem: it always shows the same person's information, no matter which poster you hover over!
84
+
85
+ ![Hovercard showing the wrong person](/img/docs/tutorial/queries-wrong-hovercard-person.png)
86
+
87
+ * * *
88
+
89
+ ## Query Variables
90
+
91
+ We need to tell the server *which* information we want more information about. GraphQL lets us define *query variables* that can be passed as arguments to specific fields. These arguments are then available on the server.
92
+
93
+ In the previous section, we saw how a field can accept arguments, but the argument values were hard-coded, e.g. `url(width: 200, height: 200)`. With query variables, we can determine these values at runtime. They’re passed from the client to the server alongside the query itself. GraphQL variables always begin with a `$` dollar sign.
94
+
95
+ Take a look inside `PosterDetailsHovercardContents.tsx`: you should see a query like this one:
96
+
97
+ ```
98
+ const PosterDetailsHovercardContentsQuery = graphql`
99
+ query PosterDetailsHovercardContentsQuery {
100
+ // color1
101
+ node(id: "1") {
102
+ // color2
103
+ ... on Actor {
104
+ ...PosterDetailsHovercardContentsBodyFragment
105
+ }
106
+ }
107
+ }
108
+ `;
109
+ ```
110
+
111
+ <p><span className="color1">The <code>node</code> field</span> is a top-level field defined in our schema that lets us fetch any graph node given its unique ID. It takes the ID as an argument, which is currently hard-coded. In this exercise, we’ll be replacing this hard-coded ID with a variable supplied by our UI state.</p>
112
+
113
+ The funny-looking `... on Actor` is a <span className="color2">*type refinement*</span>. We’ll look at these in more detail in the next section and can ignore it for now. In brief, since we could supply any ID at all to the `node` field, there’s no way to know statically what *type* of node we’d be selecting. The type refinement specifies what type we expect, allowing us to use fields from the `Actor` type.
114
+
115
+ Within that, we simply spread a fragment that contains the fields we want to show — about which more later. For now, here are the steps to take to replace this hard-coded ID with the ID of the poster we’re hovering over:
116
+
117
+ ### Step 1 — define a query variable
118
+
119
+ First we need to edit our query to declare that it accepts a query variable. Here’s the change:
120
+
121
+ ```
122
+ const PosterDetailsHovercardContentsQuery = graphql`
123
+ query PosterDetailsHovercardContentsQuery(
124
+ // change-line
125
+ $posterID: ID!
126
+ ) {
127
+ node(id: "1") {
128
+ ... on Actor {
129
+ ...PosterDetailsHovercardContentsBodyFragment
130
+ }
131
+ }
132
+ }
133
+ `;
134
+ ```
135
+
136
+ * The variable name is `$posterID`. This is the symbol we’ll use within the rest of the GraphQL query to refer to the value passed in from the UI.
137
+ * Variables have a type — in this case `ID!`*.* The `ID` type is a synonym for `String` that is used for node IDs to help distinguish them from other strings. The `!` on `ID!` means that field is non-nullable. In GraphQL, fields are normally nullable and non-nullability is the exception.
138
+
139
+ ### Step 2 — pass the variable in as a field argument
140
+
141
+ Now we replace the hard-coded `"1"` with our new variable:
142
+
143
+ ```
144
+ const PosterDetailsHovercardContentsQuery = graphql`
145
+ query PosterDetailsHovercardContentsQuery($posterID: ID!) {
146
+ node(
147
+ // change-line
148
+ id: $posterID
149
+ ) {
150
+ ... on Actor {
151
+ ...PosterDetailsHovercardContentsBodyFragment
152
+ }
153
+ }
154
+ }
155
+ `;
156
+ ```
157
+
158
+ :::note
159
+ You can use query variables not only as field arguments, but as arguments to fragments.
160
+ :::
161
+
162
+ ### Step 3 — provide the argument value to useLazyLoadQuery
163
+
164
+ Now we need to pass in the actual value from our UI at runtime. The `useLazyLoadQuery` hook’s second argument is an object with variable values. We’ll add a new prop to our component and pass its value in there:
165
+
166
+ ```
167
+ export default function PosterDetailsHovercardContents({
168
+ // change-line
169
+ posterID,
170
+ }: {
171
+ // change-line
172
+ posterID: string;
173
+ }): React.ReactElement {
174
+ const data = useLazyLoadQuery<QueryType>(
175
+ PosterDetailsHovercardContentsQuery,
176
+ // change-line
177
+ {posterID},
178
+ );
179
+ return <PosterDetailsHovercardContentsBody poster={data.node} />;
180
+ }
181
+ ```
182
+
183
+ ### Step 4 — pass the ID in from the parent component
184
+
185
+ Now we need to supply the `posterID` prop from the hovercard’s parent component, which is `PosterByline`. Head over to that file and add `id` to its fragment — then pass the ID in as a prop:
186
+
187
+ ```
188
+ const PosterBylineFragment = graphql`
189
+ fragment PosterBylineFragment on Actor {
190
+ // change-line
191
+ id
192
+ ...
193
+ }
194
+ `;
195
+
196
+ export default function PosterByline({ poster }: Props): React.ReactElement {
197
+ ...
198
+ return (
199
+ ...
200
+ <PosterDetailsHovercardContents
201
+ // change-line
202
+ posterID={data.id}
203
+ />
204
+ ...
205
+ );
206
+ }
207
+ ```
208
+
209
+ At this point, the hovercard should show the appropriate information for each poster that we hover over.
210
+
211
+ ![Hovercard showing the correct person](/img/docs/tutorial/query-variables-hovercard-correct.png)
212
+
213
+ If you use the Network inspector in your browser, you should be able to find that the variable value is being passed alongside the query:
214
+
215
+ ![Network request inspector showing variable being sent to the server](/img/docs/tutorial/network-request-with-variables.png)
216
+
217
+ You may also notice that this request is made only the first time you hover over a particular poster. Relay caches the results of the query and re-uses them after that, until eventually removing the cached data if it hasn’t been used recently.
218
+
219
+ <details>
220
+ <summary>Deep dive: Caching and the Relay Store</summary>
221
+
222
+ In contrast to most other systems, Relay’s caching is not based on queries, but on graph nodes. Relay maintains a local cache of all the nodes it has fetched called the Relay Store. Each node in the Store is identified and retrieved by its ID. If two queries ask for the same information, as identified by node IDs, then the second query will be fulfilled using the cached information retrieved for the first query, and not be fetched. Make sure to configure [missing field handlers](/docs/guided-tour/reusing-cached-data/filling-in-missing-data/) to take advantage of this caching behavior.
223
+
224
+ Relay will garbage-collect nodes from the Store if they aren’t “reachable” from any queries that are used, or have been recently used, by any mounted components.
225
+ </details>
226
+
227
+ <details>
228
+ <summary>Deep dive: Why GraphQL Needs a Syntax for Variables</summary>
229
+
230
+ You might be wondering why GraphQL even has the concept of variables, instead of just interpolating the value of the variables into the query string. Well, [as mentioned before](../queries-1), the text of the GraphQL query string isn’t available at runtime, because Relay replaces it with a data structure that is more efficient. You can also configure Relay to use *prepared queries*, where the compiler uploads each query to the server at build time and assigns it an ID — in that case, at runtime, Relay is just telling the server “Give me query #1337”, so string interpolation isn't possible and therefore the variables have to come out of band. Even when the query string is available, passing variable values separately eliminates any issues with serializing arbitrary values and escaping strings, above what is required with any HTTP request.
231
+ </details>
232
+
233
+ * * *
234
+
235
+ ## Preloaded Queries
236
+
237
+ This example app is very simple, so performance isn't an issue. (In fact, the server is artificially slowed down in order to make loading states perceptible.) However, one of Relay's main concerns is to make performance as fast as possible in real apps.
238
+
239
+ Right now, the hovercard uses the `useLazyLoadQuery` hook, which fetches the query when the component is rendered. That means the timeline looks something like this:
240
+
241
+ ![Network doesn't start until render](/img/docs/tutorial/preloaded-basic.png)
242
+
243
+ Ideally, we should start the network fetch as early as possible, but here we don't start it until React is finished rendering. This timeline could look even worse if we used `React.lazy` to load the code for the hovercard component itself when the interaction happened. In that case, it would look like this:
244
+
245
+ ![Network doesn't start until component is fetched and then rendered](/img/docs/tutorial/preloaded-lazy.png)
246
+
247
+ Notice how we’re waiting around before we even start fetching the GraphQL query. It would be better if the query fetch began before the React component even rendered, right at the beginning in the mouse event handler itself. Then the timeline would look like this:
248
+
249
+ ![Network and component fetch happen concurrently](/img/docs/tutorial/preloaded-ideal.png)
250
+
251
+ When the user interacts, we should immediately start fetching the query we need, while also beginning to render the component (fetching its code first if needed). Once both of these async processes are complete, we can render the component with the data available and show it to the user.
252
+
253
+ Relay provides a feature called *preloaded queries* that let us do this.
254
+
255
+ Let’s modify the hovercard to use preloaded queries.
256
+
257
+ ### Step 1 — change useLazyLoadQuery to usePreloadedQuery
258
+
259
+ As a reminder, this is the `PosterDetailsHovercardContents` component that currently fetches the data lazily:
260
+
261
+ ```
262
+ export default function PosterDetailsHovercardContents({
263
+ posterID,
264
+ }: {
265
+ posterID: string;
266
+ }): React.ReactElement {
267
+ const data = useLazyLoadQuery<QueryType>(
268
+ PosterDetailsHovercardContentsQuery,
269
+ {posterID},
270
+ );
271
+ return <PosterDetailsHovercardContentsBody poster={data.node} />;
272
+ }
273
+ ```
274
+
275
+ It calls `useLazyLoadQuery` which accepts *variables* as its second argument. We want to change this to `usePreloadedQuery`. However, with preloaded queries, the variables are actually determined when the query is fetched, which will be before this component is even rendered. So instead of variables, this hook takes a *query reference* that contains the information it needs to retrieve the results of the query. The query reference will be created when we fetch the query in Step 2.
276
+
277
+ Change the component as follows:
278
+
279
+ ```
280
+ import {usePreloadedQuery} from 'react-relay';
281
+ import type {PreloadedQuery} from 'react-relay';
282
+ import type {PosterDetailsHovercardContentsQuery as QueryType} from './__generated__/PosterDetailsHovercardContentsQuery.graphql';
283
+
284
+ export default function PosterDetailsHovercardContents({
285
+ // change-line
286
+ queryRef,
287
+ }: {
288
+ // change-line
289
+ queryRef: PreloadedQuery<QueryType>,
290
+ }): React.ReactElement {
291
+ // change-line
292
+ const data = usePreloadedQuery(
293
+ PosterDetailsHovercardContentsQuery,
294
+ // change-line
295
+ queryRef,
296
+ );
297
+ ...
298
+ }
299
+ ```
300
+
301
+ :::note
302
+ We’ll be modifying the parent component, `PosterByline`, to have it initiate the `PosterDetailsHovercardContentsQuery` query. Since it needs a reference to the query, the query needs to be exported:
303
+ ```
304
+ export const PosterDetailsHovercardContentsQuery = graphql`...
305
+ ```
306
+ :::
307
+
308
+
309
+ ### Step 2 — Call useQueryLoader in the parent component
310
+
311
+ Now that `PosterDetailsHovercardContents` expects a query ref, we need to create that query ref and pass it down from the parent component, which is `PosterByline`. We create the query ref using a hook called `useQueryLoader`. This hook also returns a function that we call in our event handler to trigger the query fetch.
312
+
313
+ ```
314
+ import {useQueryLoader} from 'react-relay';
315
+ import type {PosterDetailsHovercardContentsQuery as HovercardQueryType} from './__generated__/PosterDetailsHovercardContentsQuery.graphql';
316
+ import {PosterDetailsHovercardContentsQuery} from './PosterDetailsHovercardContents';
317
+
318
+ export default function PosterByline({ poster }: Props): React.ReactElement {
319
+ ...
320
+ // change
321
+ const [
322
+ hovercardQueryRef,
323
+ loadHovercardQuery,
324
+ ] = useQueryLoader<HovercardQueryType>(PosterDetailsHovercardContentsQuery);
325
+ // end-change
326
+ return (
327
+ ...
328
+ <PosterDetailsHovercardContents
329
+ // change-line
330
+ queryRef={hovercardQueryRef}
331
+ />
332
+ ...
333
+ );
334
+ }
335
+ ```
336
+
337
+ The `useQueryLoader` hook returns two things we need:
338
+
339
+ * The query ref is an opaque piece of information that `usePreloadedQuery` will use to retrieve the result of the query.
340
+ * `loadHovercardQuery` is a function that will initiate the request.
341
+
342
+ ### Step 3 — Fetch the query in the event handler
343
+
344
+ Finally, we need to call `loadHovercardQuery` in an event handler that happens when the card is shown. Luckily the `Hovercard` component has a `onBeginHover` event that we can use:
345
+
346
+ ```
347
+ export default function PosterByline({ poster }: Props): React.ReactElement {
348
+ ...
349
+ const [
350
+ hovercardQueryRef,
351
+ loadHovercardQuery,
352
+ ] = useQueryLoader<HovercardQueryType>(PosterDetailsHovercardContentsQuery);
353
+ // change
354
+ function onBeginHover() {
355
+ loadHovercardQuery({posterID: data.id});
356
+ }
357
+ // end-change
358
+ return (
359
+ <div className="byline">
360
+ ...
361
+ <Hovercard
362
+ // change-line
363
+ onBeginHover={onBeginHover}
364
+ targetRef={hoverRef}>
365
+ <PosterDetailsHovercardContents queryRef={hovercardQueryRef} />
366
+ </Hovercard>
367
+ </div>
368
+ );
369
+ }
370
+ ```
371
+
372
+ Note that the query variables are now passed in here where we initiate the request.
373
+
374
+ At this point, you should see the same behavior as before, but now it will be a little bit faster since Relay can get the query started earlier.
375
+
376
+ :::tip
377
+ Although we introduced queries using `useLazyLoadQuery` for simplicity, preloaded queries are always the preferred way to use queries in Relay because they can significantly improve performance in the real world. With the appropriate [integrations with your server and router system](https://github.com/relayjs/relay-examples/tree/main/issue-tracker-next-v13), you can even preload the main query for a webpage on the server side before you’ve even downloaded or run any client code.
378
+ :::
379
+
380
+ * * *
381
+
382
+ ## Summary
383
+
384
+ * Although all of the data initially shown on a screen should be combined into one query, user interactions needing further information can be handled with secondary queries.
385
+ * Query variables let you pass information to the server along with your query.
386
+ * Query variables are used by passing them into field arguments.
387
+ * Preloaded queries are always the best way to go. For user interaction queries, initiate the fetch in the event handler. For the initial query for your screen, initiate the fetch as early as possible in your specific routing system. Use lazy-loaded queries only for quick prototyping, or not at all.
388
+
389
+ Next we'll briefly look at a way to enhance the hovercard by handling different types of posters differently. After that, we'll see how to handle situations where information that's part of the initial query also needs to be updated and refetched with different variables.