relay-runtime 20.1.1 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/experimental.js +1 -1
- package/experimental.js.flow +8 -8
- package/handlers/connection/ConnectionHandler.js.flow +5 -5
- package/handlers/connection/ConnectionInterface.js.flow +1 -1
- package/index.js +1 -1
- package/index.js.flow +125 -62
- package/lib/experimental.js +3 -3
- package/lib/index.js +105 -57
- package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
- package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
- package/lib/mutations/commitMutation.js +8 -8
- package/lib/mutations/validateMutation.js +4 -4
- package/lib/query/GraphQLTag.js +3 -3
- package/lib/query/fetchQuery.js +15 -3
- package/lib/store/DataChecker.js +38 -4
- package/lib/store/NormalizationEngine.js +373 -0
- package/lib/store/OperationExecutor.js +172 -113
- package/lib/store/RelayConcreteVariables.js +1 -1
- package/lib/store/RelayErrorTrie.js +2 -2
- package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
- package/lib/store/RelayModernEnvironment.js +26 -19
- package/lib/store/RelayModernRecord.js +18 -8
- package/lib/store/RelayModernSelector.js +9 -9
- package/lib/store/RelayModernStore.js +152 -43
- package/lib/store/RelayPublishQueue.js +1 -1
- package/lib/store/RelayReader.js +76 -38
- package/lib/store/RelayRecordSource.js +6 -0
- package/lib/store/RelayReferenceMarker.js +2 -1
- package/lib/store/RelayResponseNormalizer.js +88 -55
- package/lib/store/RelayStoreSubscriptions.js +34 -10
- package/lib/store/RelayStoreUtils.js +8 -1
- package/lib/store/ResolverFragments.js +2 -2
- package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
- package/lib/store/observeFragmentExperimental.js +17 -1
- package/lib/store/observeQueryExperimental.js +2 -2
- package/lib/subscription/requestSubscription.js +3 -3
- package/lib/util/RelayError.js +3 -0
- package/lib/util/RelayFeatureFlags.js +6 -2
- package/lib/util/RelayReplaySubject.js +4 -4
- package/lib/util/handlePotentialSnapshotErrors.js +2 -2
- package/lib/util/stableCopy.js +2 -2
- package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
- package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
- package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
- package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
- package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
- package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
- package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
- package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
- package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
- package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
- package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
- package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
- package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
- package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
- package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
- package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
- package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
- package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
- package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
- package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +113 -0
- package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
- package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
- package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
- package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
- package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
- package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
- package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
- package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
- package/llm-docs/api-reference/types/Disposable.mdx +4 -0
- package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
- package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
- package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
- package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
- package/llm-docs/community/learning-resources.mdx +64 -0
- package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
- package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
- package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
- package/llm-docs/debugging/relay-devtools.mdx +73 -0
- package/llm-docs/debugging/why-null.mdx +116 -0
- package/llm-docs/editor-support.mdx +55 -0
- package/llm-docs/error-reference/unknown-field.mdx +36 -0
- package/llm-docs/getting-started/babel-plugin.mdx +31 -0
- package/llm-docs/getting-started/compiler-config.mdx +25 -0
- package/llm-docs/getting-started/compiler.mdx +82 -0
- package/llm-docs/getting-started/lint-rules.mdx +87 -0
- package/llm-docs/getting-started/production.mdx +30 -0
- package/llm-docs/getting-started/quick-start.mdx +213 -0
- package/llm-docs/glossary/glossary.mdx +1040 -0
- package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
- package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
- package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
- package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
- package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
- package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
- package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
- package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
- package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
- package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
- package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
- package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
- package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
- package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
- package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
- package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
- package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
- package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
- package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
- package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
- package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
- package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
- package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
- package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
- package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
- package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
- package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
- package/llm-docs/guides/alias-directive.mdx +160 -0
- package/llm-docs/guides/catch-directive.mdx +167 -0
- package/llm-docs/guides/client-schema-extensions.mdx +208 -0
- package/llm-docs/guides/codemods.mdx +66 -0
- package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
- package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
- package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
- package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
- package/llm-docs/guides/document-comparison.mdx +106 -0
- package/llm-docs/guides/graphql-server-specification.mdx +453 -0
- package/llm-docs/guides/network-layer.mdx +69 -0
- package/llm-docs/guides/persisted-queries.mdx +328 -0
- package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
- package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
- package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
- package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
- package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
- package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
- package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
- package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
- package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
- package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
- package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
- package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
- package/llm-docs/guides/required-directive.mdx +240 -0
- package/llm-docs/guides/semantic-nullability.mdx +93 -0
- package/llm-docs/guides/testing-relay-components.mdx +642 -0
- package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
- package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
- package/llm-docs/guides/type-emission.mdx +414 -0
- package/llm-docs/home.mdx +32 -0
- package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
- package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
- package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
- package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
- package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
- package/llm-docs/principles-and-architecture/videos.mdx +50 -0
- package/llm-docs/tutorial/arrays-lists.mdx +126 -0
- package/llm-docs/tutorial/fragments-1.mdx +487 -0
- package/llm-docs/tutorial/graphql.mdx +172 -0
- package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
- package/llm-docs/tutorial/intro.mdx +58 -0
- package/llm-docs/tutorial/mutations-updates.mdx +624 -0
- package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
- package/llm-docs/tutorial/queries-1.mdx +267 -0
- package/llm-docs/tutorial/queries-2.mdx +389 -0
- package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +7 -7
- package/multi-actor-environment/ActorUtils.js.flow +1 -1
- package/multi-actor-environment/MultiActorEnvironment.js.flow +12 -8
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +3 -3
- package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
- package/mutations/RelayRecordProxy.js.flow +8 -11
- package/mutations/RelayRecordSourceMutator.js.flow +4 -4
- package/mutations/RelayRecordSourceProxy.js.flow +4 -4
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
- package/mutations/applyOptimisticMutation.js.flow +2 -2
- package/mutations/commitMutation.js.flow +20 -16
- package/mutations/createUpdatableProxy.js.flow +19 -19
- package/mutations/readUpdatableFragment.js.flow +3 -3
- package/mutations/readUpdatableQuery.js.flow +3 -3
- package/mutations/validateMutation.js.flow +7 -7
- package/network/RelayNetworkTypes.js.flow +4 -4
- package/network/RelayObservable.js.flow +16 -14
- package/network/RelayQueryResponseCache.js.flow +3 -3
- package/network/wrapNetworkWithLogObserver.js.flow +1 -1
- package/package.json +2 -1
- package/query/GraphQLTag.js.flow +22 -10
- package/query/fetchQuery.js.flow +23 -10
- package/query/fetchQuery_DEPRECATED.js.flow +1 -1
- package/store/DataChecker.js.flow +43 -9
- package/store/NormalizationEngine.js.flow +779 -0
- package/store/OperationExecutor.js.flow +173 -70
- package/store/RelayConcreteVariables.js.flow +5 -5
- package/store/RelayErrorTrie.js.flow +12 -12
- package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
- package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
- package/store/RelayModernEnvironment.js.flow +41 -26
- package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
- package/store/RelayModernOperationDescriptor.js.flow +1 -1
- package/store/RelayModernRecord.js.flow +44 -20
- package/store/RelayModernSelector.js.flow +21 -21
- package/store/RelayModernStore.js.flow +219 -58
- package/store/RelayOperationTracker.js.flow +2 -2
- package/store/RelayOptimisticRecordSource.js.flow +2 -2
- package/store/RelayPublishQueue.js.flow +21 -12
- package/store/RelayReader.js.flow +129 -57
- package/store/RelayRecordSource.js.flow +10 -0
- package/store/RelayRecordState.js.flow +1 -1
- package/store/RelayReferenceMarker.js.flow +5 -4
- package/store/RelayResponseNormalizer.js.flow +125 -57
- package/store/RelayStoreSubscriptions.js.flow +52 -8
- package/store/RelayStoreTypes.js.flow +153 -64
- package/store/RelayStoreUtils.js.flow +15 -7
- package/store/ResolverCache.js.flow +2 -2
- package/store/ResolverFragments.js.flow +12 -12
- package/store/StoreInspector.js.flow +6 -7
- package/store/cloneRelayHandleSourceField.js.flow +1 -1
- package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
- package/store/createRelayContext.js.flow +1 -1
- package/store/createRelayLoggingContext.js.flow +4 -4
- package/store/defaultGetDataID.js.flow +2 -2
- package/store/isRelayModernEnvironment.js.flow +4 -2
- package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
- package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
- package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
- package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
- package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
- package/store/observeFragmentExperimental.js.flow +49 -20
- package/store/observeQueryExperimental.js.flow +5 -5
- package/store/readInlineData.js.flow +4 -4
- package/store/waitForFragmentExperimental.js.flow +3 -3
- package/subscription/requestSubscription.js.flow +7 -7
- package/util/NormalizationNode.js.flow +34 -32
- package/util/ReaderNode.js.flow +32 -30
- package/util/RelayConcreteNode.js.flow +5 -5
- package/util/RelayError.js.flow +4 -1
- package/util/RelayFeatureFlags.js.flow +21 -1
- package/util/RelayProfiler.js.flow +1 -1
- package/util/RelayReplaySubject.js.flow +3 -3
- package/util/RelayRuntimeTypes.js.flow +11 -11
- package/util/createPayloadFor3DField.js.flow +9 -5
- package/util/deepFreeze.js.flow +2 -2
- package/util/getFragmentIdentifier.js.flow +1 -1
- package/util/getPaginationMetadata.js.flow +1 -1
- package/util/getPaginationVariables.js.flow +1 -1
- package/util/getPendingOperationsForFragment.js.flow +2 -2
- package/util/getRefetchMetadata.js.flow +6 -5
- package/util/getValueAtPath.js.flow +3 -3
- package/util/handlePotentialSnapshotErrors.js.flow +5 -5
- package/util/isEmptyObject.js.flow +1 -1
- package/util/isPromise.js.flow +2 -2
- package/util/isScalarAndEqual.js.flow +1 -1
- package/util/recycleNodesInto.js.flow +2 -2
- package/util/registerEnvironmentWithDevTools.js.flow +1 -1
- package/util/shallowFreeze.js.flow +1 -1
- package/util/stableCopy.js.flow +5 -5
- package/util/withProvidedVariables.js.flow +14 -10
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: testing-relay-with-preloaded-queries
|
|
3
|
+
title: Testing Relay with Preloaded Queries
|
|
4
|
+
slug: /guides/testing-relay-with-preloaded-queries/
|
|
5
|
+
description: Relay guide to testing with preloaded queries
|
|
6
|
+
keywords:
|
|
7
|
+
- testing
|
|
8
|
+
- preloaded
|
|
9
|
+
- usePreloadedQuery
|
|
10
|
+
- queueOperationResolver
|
|
11
|
+
- queuePendingOperation
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
15
|
+
import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
|
|
16
|
+
|
|
17
|
+
Components that use preloaded queries (`useQueryLoader` and `usePreloadedQuery` hooks) require slightly different and more convoluted test setup.
|
|
18
|
+
|
|
19
|
+
In short, there are two steps that need to be performed **before rendering the component**
|
|
20
|
+
|
|
21
|
+
1. Configure the query resolver to generate the response via `environment.mock.queueOperationResolver`
|
|
22
|
+
2. Record a pending queue invocation via `environment.mock.queuePendingOperation`
|
|
23
|
+
|
|
24
|
+
## Symptoms that something is wrong
|
|
25
|
+
|
|
26
|
+
1. The test doesn't do what is expected from it.
|
|
27
|
+
2. The query seems to be blocking instead of executing
|
|
28
|
+
1. E.g. the `Suspend` doesn't switch from "waiting" to "data loaded" state
|
|
29
|
+
3. If you add the `console.log` before and after `usePreloadedQuery`, only the "before" call is hit
|
|
30
|
+
|
|
31
|
+
## TL;DR
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
const {RelayEnvironmentProvider} = require('react-relay');
|
|
35
|
+
const {MockPayloadGenerator, createMockEnvironment} = require('relay-test-utils');
|
|
36
|
+
const {act, render} = require('@testing-library/react');
|
|
37
|
+
test("...", () => {
|
|
38
|
+
// arrange
|
|
39
|
+
const environment = createMockEnvironment();
|
|
40
|
+
environment.mock.queueOperationResolver(operation => {
|
|
41
|
+
return MockPayloadGenerator.generate(operation, {
|
|
42
|
+
CurrencyAmount() {
|
|
43
|
+
return {
|
|
44
|
+
formatted_amount: "1234$",
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
const query = YourComponentGraphQLQueryGoesHere; // can be the same, or just identical
|
|
50
|
+
const variables = {
|
|
51
|
+
// ACTUAL variables for the invocation goes here
|
|
52
|
+
};
|
|
53
|
+
environment.mock.queuePendingOperation(YourComponentGraphQLQuery, variables);
|
|
54
|
+
|
|
55
|
+
// act
|
|
56
|
+
const {getByTestId, ..otherStuffYouMightNeed} = render(
|
|
57
|
+
<RelayEnvironmentProvider environment={environment}>
|
|
58
|
+
<YourComponent data-testid="1234" {...componentPropsIfAny}/>
|
|
59
|
+
</RelayEnvironmentProvider>
|
|
60
|
+
);
|
|
61
|
+
// trigger the loading - click a button, emit an event, etc. or ...
|
|
62
|
+
act(() => jest.runAllImmediates()); // ... if loadQuery is in the useEffect()
|
|
63
|
+
// assert
|
|
64
|
+
// your assertions go here
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Configure the query resolver to generate the response
|
|
69
|
+
|
|
70
|
+
This is done via `environment.mock.queueOperationResolver(operation)` call, but getting it right might be tricky.
|
|
71
|
+
|
|
72
|
+
The crux of this call is to return a mocked graphql result in a very particular format (as `MockResolvers` type, to be precise). This is done via a second parameter to `generate` - it is an object, whose keys are GraphQL types that we want to mock. (See [`mock-payload-generator`](../testing-relay-components/#mock-payload-generator-and-the-relay_test_operation-directive)).
|
|
73
|
+
|
|
74
|
+
Continuing on the above example:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
return MockPayloadGenerator.generate(operation, {
|
|
78
|
+
CurrencyAmount() { // <-- the GraphQL type
|
|
79
|
+
return {
|
|
80
|
+
formatted_amount: "response_value" <-- CurrencyAmount fields, selected in the query
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
The tricky thing here is to obtain the name of the GraphQL type and fields to return. This can be done in two ways:
|
|
86
|
+
|
|
87
|
+
* Call `console.log(JSON.stringify(operation, null, 2))` and look for the `concreteType` that corresponds to what we want to mock. Then look at the sibling `selections` array, which describes the fields that are selected from that object.
|
|
88
|
+
|
|
89
|
+
<FbInternalOnly>
|
|
90
|
+
|
|
91
|
+
* This is somewhat intense - P139017123 is the output for [this query](https://fburl.com/diffusion/irqurgj9). Rule of thumb - one nested call in the query produces one nested object in the output.
|
|
92
|
+
* Look up the type in the graphiql (bunnylol graphiql), then specify the fields listed on the query.
|
|
93
|
+
|
|
94
|
+
:::note
|
|
95
|
+
The type you need seems to be the type returned by the *innermost function call* (or calls, if you have multiple functions called in one query - see D23078476). This needs to be confirmed - in both example diffs the target types was also leafs.
|
|
96
|
+
:::
|
|
97
|
+
|
|
98
|
+
</FbInternalOnly>
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
It is **possible** to return different data for different query variables via [Mock Resolver Context](../testing-relay-components/#mock-resolver-context). The query variables will be available on the `context.args`, but only to the *innermost function call* (for the query above, only `offer_ids` are available)
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
CurrencyAmount(context) {
|
|
105
|
+
console.log(JSON.stringify(context, null, 2)); // <--
|
|
106
|
+
return { formatted_amount: mockResponse }
|
|
107
|
+
}
|
|
108
|
+
// <-- logs { ...snip..., "name": "subtotal_price_for_offers", args: { offer_ids: [...] } }
|
|
109
|
+
```
|
|
110
|
+
### Record a pending queue invocation
|
|
111
|
+
|
|
112
|
+
This is more straightforward - it is done via a call to `environment.mock.queuePendingOperation(query, variables)`
|
|
113
|
+
|
|
114
|
+
* `Query` needs to match the query issues by the component. Simplest (and most robust against query changes) is to export the query from the component module and use it in the test, but having an *identical* (but not the same) query works as well.
|
|
115
|
+
* `variables` has to match the variables that will be used in this test invocation.
|
|
116
|
+
* Beware of nested objects and arrays - they are compared via `areEqual` ([invocation code](https://github.com/facebook/relay/blob/046f758c6b411608371d4cc2f0a594ced331864e/packages/relay-test-utils/RelayModernMockEnvironment.js#L233))
|
|
117
|
+
* Arrays are compared by values (not by reference), but the order of elements matter
|
|
118
|
+
* Nested objects - performs deep compare, order of keys is not relevant (this is not confirmed - please update this doc if you used a graphql query with "deep" structure*)*
|
|
119
|
+
|
|
120
|
+
<FbInternalOnly>
|
|
121
|
+
|
|
122
|
+
### Example diffs
|
|
123
|
+
|
|
124
|
+
* [D23078476](https://internalfb.com/intern/diff/D23078476)
|
|
125
|
+
* [D23101739](https://www.internalfb.com/diff/D23101739)
|
|
126
|
+
|
|
127
|
+
</FbInternalOnly>
|
|
128
|
+
|
|
129
|
+
## Troubleshooting
|
|
130
|
+
|
|
131
|
+
* `console.log`, `console.log` everywhere! Recommended places:
|
|
132
|
+
* component: before and after `useQueryLoader, usePreloadedQuery, loadQuery`
|
|
133
|
+
* test: in `queueOperationResolver` callback
|
|
134
|
+
* library: in `RelayModernMockEnvironment.execute`, after the `const currentOperation = ...` call ([here](https://github.com/facebook/relay/blob/046f758c6b411608371d4cc2f0a594ced331864e/packages/relay-test-utils/RelayModernMockEnvironment.js#L230))
|
|
135
|
+
* If `loadQuery` is not called - make sure to issue the triggering event. Depending on your component implementation it could be a user-action (like button click or key press), javascript event (via event emitter mechanisms) or a simple "delayed execution" with `useEffect`.
|
|
136
|
+
* The `useEffect` case is probably easiest to miss - make sure to call `act(() => jest.runAllImmediates())` **after** rendering the component
|
|
137
|
+
* If "before" `usePreloadedQuery` is hit, but "after" is not - the query suspends. This entire guide is written to resolve it - you might want to re-read it. But most likely it is either:
|
|
138
|
+
* Used a different query - the query resolver would not be called, `currentOperation` will be `null`
|
|
139
|
+
* Query variables don't match - the query resolver would not be called, `currentOperation` will be `null` (make sure to inspect the `variables`).
|
|
140
|
+
* Also, make sure arrays are in the same order, if any (or better yet, use sets, if at all possible).
|
|
141
|
+
* If data returned from the query is not what you expect, make sure you're generating the right graphql type.
|
|
142
|
+
* You can tell you're mocking the wrong one if the return values look something like `<mock-value-for-field-"formatted_amount">`
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
:::note
|
|
146
|
+
Make sure the component and the test use the same environment (i.e. there's no `<RelayEnvironmentProvider environment={RelayFBEnvironment}>` somewhere nested in your test React tree.
|
|
147
|
+
:::
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## Epilogue
|
|
151
|
+
|
|
152
|
+
Examples here use `testing-library-react`, but it works with the `react-test-renderer` as well.
|
|
153
|
+
|
|
154
|
+
<FbInternalOnly>
|
|
155
|
+
|
|
156
|
+
See [D23078476](https://www.internalfb.com/diff/D23078476).
|
|
157
|
+
|
|
158
|
+
</FbInternalOnly>
|
|
159
|
+
|
|
160
|
+
<DocsRating />
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: throw-on-field-error-directive
|
|
3
|
+
title: '@throwOnFieldError Directive'
|
|
4
|
+
slug: /guides/throw-on-field-error-directive/
|
|
5
|
+
description: Relay guide to @throwOnFieldError
|
|
6
|
+
keywords:
|
|
7
|
+
- directive
|
|
8
|
+
- optional
|
|
9
|
+
- errors
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
13
|
+
|
|
14
|
+
:::tip
|
|
15
|
+
Both `@catch` and `@throwOnFieldError` only handle field errors in the
|
|
16
|
+
query/fragment/mutation in which they are used. They **do not** handle errors
|
|
17
|
+
related to fields in any spread fragments.
|
|
18
|
+
:::
|
|
19
|
+
|
|
20
|
+
The `@throwOnFieldError` directive can be added to fragments and queries. When
|
|
21
|
+
this directive is used, the Relay runtime will throw an exception if a field
|
|
22
|
+
with a field error is encountered while reading the fragment or query, or if
|
|
23
|
+
Relay is missing data due to a
|
|
24
|
+
[graph relationship change](../debugging/why-null.mdx#graph-relationship-change).
|
|
25
|
+
|
|
26
|
+
In addition to causing the Relay runtime to throw an exception if a field error
|
|
27
|
+
is encountered, the `@throwOnFieldError` directive also enables generation of
|
|
28
|
+
non-null Flow types for fields that have the `@semanticNonNull` directive in the
|
|
29
|
+
schema. This means that if a field has the `@semanticNonNull` directive, the
|
|
30
|
+
generated Flow type for that field will be non-nullable; if an error were to
|
|
31
|
+
occur while reading that field, the thrown exception will prevent your
|
|
32
|
+
application from receiving a null value. Making a previously-nullable field
|
|
33
|
+
non-null may make many existing `@required` directives unnecessary; you can use
|
|
34
|
+
the
|
|
35
|
+
[remove-unnecessary-required-directives codemod](../guides/codemods.mdx#remove-unnecessary-required-directives)
|
|
36
|
+
to clean these up.
|
|
37
|
+
|
|
38
|
+
To use the `@throwOnFieldError` directive, add it to a fragment or query in your
|
|
39
|
+
Relay code. For example:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
fragment MyFragment on User @throwOnFieldError {
|
|
43
|
+
id
|
|
44
|
+
name
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
In this example, the `@throwOnFieldError` directive is added to the MyFragment
|
|
49
|
+
fragment. If any of the fields in this fragment (in this case, id and name) have
|
|
50
|
+
a field error, the Relay runtime will throw an exception at the time the
|
|
51
|
+
fragment is read.
|
|
52
|
+
|
|
53
|
+
If you wish to handle a specific field error locally within your
|
|
54
|
+
`@throwOnFieldError` fragment or query instead of having that error throw, you
|
|
55
|
+
can catch the error with [@catch](./catch-directive.mdx).
|
|
56
|
+
|
|
57
|
+
**Read more about Relay's experimental support for
|
|
58
|
+
[Semantic Nullability](./semantic-nullability.mdx).**
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: type-emission
|
|
3
|
+
title: Type Emission
|
|
4
|
+
slug: /guides/type-emission/
|
|
5
|
+
description: Relay guide to type emission
|
|
6
|
+
keywords:
|
|
7
|
+
- type emission
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
11
|
+
import {FbInternalOnly, OssOnly, fbContent} from 'docusaurus-plugin-internaldocs-fb/internal';
|
|
12
|
+
import Tabs from '@theme/Tabs';
|
|
13
|
+
import TabItem from '@theme/TabItem';
|
|
14
|
+
|
|
15
|
+
As part of its normal work, the [**Relay Compiler**](../compiler) will emit type information for your language of choice that helps you write type-safe application code. These types are included in the artifacts that `relay-compiler` generates to describe your operations and fragments.
|
|
16
|
+
|
|
17
|
+
## Operation variables
|
|
18
|
+
|
|
19
|
+
The shape of the variables object used for query, mutation, or subscription operations.
|
|
20
|
+
|
|
21
|
+
In this example the emitted type-information would require the variables object to contain an `artistID` key with a non-null string.
|
|
22
|
+
|
|
23
|
+
<Tabs
|
|
24
|
+
defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
|
|
25
|
+
values={[
|
|
26
|
+
{label: 'Flow', value: 'Flow'},
|
|
27
|
+
{label: 'TypeScript', value: 'TypeScript'},
|
|
28
|
+
]}>
|
|
29
|
+
<TabItem value="Flow">
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
/**
|
|
33
|
+
* export type ExampleQuery$variables = {
|
|
34
|
+
* +artistID: string,
|
|
35
|
+
* }
|
|
36
|
+
* export type ExampleQuery$data = {
|
|
37
|
+
* +artist: {
|
|
38
|
+
* +name: ?string,
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
* export type ExampleQuery = {
|
|
42
|
+
* +variables: ExampleQuery$variables,
|
|
43
|
+
* +response: ExampleQuery$data,
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
const data = useLazyLoadQuery(
|
|
48
|
+
graphql`
|
|
49
|
+
query ExampleQuery($artistID: ID!) {
|
|
50
|
+
artist(id: $artistID) {
|
|
51
|
+
name
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`,
|
|
55
|
+
// variables are expected to be of type ExampleQuery$variables
|
|
56
|
+
{artistID: 'banksy'},
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
</TabItem>
|
|
61
|
+
|
|
62
|
+
<TabItem value="TypeScript">
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
/**
|
|
66
|
+
* export type ExampleQuery$variables = {
|
|
67
|
+
* readonly artistID: string
|
|
68
|
+
* }
|
|
69
|
+
* export type ExampleQuery$data = {
|
|
70
|
+
* readonly artist?: {
|
|
71
|
+
* readonly name?: string
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
* export type ExampleQuery = {
|
|
75
|
+
* readonly variables: ExampleQuery$variables
|
|
76
|
+
* readonly response: ExampleQuery$data
|
|
77
|
+
* }
|
|
78
|
+
*/
|
|
79
|
+
const data = useLazyLoadQuery(
|
|
80
|
+
graphql`
|
|
81
|
+
query ExampleQuery($artistID: ID!) {
|
|
82
|
+
artist(id: $artistID) {
|
|
83
|
+
name
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`,
|
|
87
|
+
// variables are expected to be of type ExampleQuery$variables
|
|
88
|
+
{artistID: 'banksy'},
|
|
89
|
+
);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
</TabItem>
|
|
93
|
+
</Tabs>
|
|
94
|
+
|
|
95
|
+
## Operation and fragment data
|
|
96
|
+
|
|
97
|
+
The shape of the data selected in a operation or fragment, following the [data-masking] rules. That is, excluding any data selected by fragment spreads.
|
|
98
|
+
|
|
99
|
+
In this example the emitted type-information describes the response data which is returned by `useLazyLoadQuery` (or `usePreloadedQuery`).
|
|
100
|
+
|
|
101
|
+
<Tabs
|
|
102
|
+
defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
|
|
103
|
+
values={[
|
|
104
|
+
{label: 'Flow', value: 'Flow'},
|
|
105
|
+
{label: 'TypeScript', value: 'TypeScript'},
|
|
106
|
+
]}>
|
|
107
|
+
<TabItem value="Flow">
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
/**
|
|
111
|
+
* export type ExampleQuery$variables = {
|
|
112
|
+
* +artistID: string,
|
|
113
|
+
* }
|
|
114
|
+
* export type ExampleQuery$data = {
|
|
115
|
+
* +artist: {
|
|
116
|
+
* +name: ?string,
|
|
117
|
+
* }
|
|
118
|
+
* }
|
|
119
|
+
* export type ExampleQuery = {
|
|
120
|
+
* +variables: ExampleQuery$variables,
|
|
121
|
+
* +response: ExampleQuery$data,
|
|
122
|
+
* }
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
// data is of type ExampleQuery$data
|
|
126
|
+
const data = useLazyLoadQuery(
|
|
127
|
+
graphql`
|
|
128
|
+
query ExampleQuery($artistID: ID!) {
|
|
129
|
+
artist(id: $artistID) {
|
|
130
|
+
name
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
`,
|
|
134
|
+
{artistID: 'banksy'},
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return props.artist && <div>{props.artist.name} is great!</div>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
</TabItem>
|
|
141
|
+
|
|
142
|
+
<TabItem value="TypeScript">
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
/**
|
|
146
|
+
* export type ExampleQuery$variables = {
|
|
147
|
+
* readonly artistID: string
|
|
148
|
+
* }
|
|
149
|
+
* export type ExampleQuery$data = {
|
|
150
|
+
* readonly artist?: {
|
|
151
|
+
* readonly name?: string
|
|
152
|
+
* }
|
|
153
|
+
* }
|
|
154
|
+
* export type ExampleQuery = {
|
|
155
|
+
* readonly variables: ExampleQuery$variables
|
|
156
|
+
* readonly response: ExampleQuery$data
|
|
157
|
+
* }
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
// data is of type ExampleQuery$data
|
|
161
|
+
const data = useLazyLoadQuery(
|
|
162
|
+
graphql`
|
|
163
|
+
query ExampleQuery($artistID: ID!) {
|
|
164
|
+
artist(id: $artistID) {
|
|
165
|
+
name
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
`,
|
|
169
|
+
{artistID: 'banksy'},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return props.artist && <div>{props.artist.name} is great!</div>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
</TabItem>
|
|
176
|
+
</Tabs>
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
Similarly, in this example the emitted type-information describes the type of the prop to match the type of the fragment reference `useFragment` expects to receive.
|
|
180
|
+
|
|
181
|
+
<Tabs
|
|
182
|
+
defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
|
|
183
|
+
values={[
|
|
184
|
+
{label: 'Flow', value: 'Flow'},
|
|
185
|
+
{label: 'TypeScript', value: 'TypeScript'},
|
|
186
|
+
]}>
|
|
187
|
+
<TabItem value="Flow">
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
/**
|
|
191
|
+
* export type ExampleFragmentComponent_artist$data = {
|
|
192
|
+
* +name: string
|
|
193
|
+
* }
|
|
194
|
+
*
|
|
195
|
+
* export type ExampleFragmentComponent_artist$key = { ... }
|
|
196
|
+
*/
|
|
197
|
+
|
|
198
|
+
import type { ExampleFragmentComponent_artist$key } from "__generated__/ExampleFragmentComponent_artist.graphql"
|
|
199
|
+
|
|
200
|
+
type Props = {
|
|
201
|
+
artist: ExampleFragmentComponent_artist$key,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default ExampleFragmentComponent(props) {
|
|
205
|
+
// data is of type ExampleFragmentComponent_artist$data
|
|
206
|
+
const data = useFragment(
|
|
207
|
+
graphql`
|
|
208
|
+
fragment ExampleFragmentComponent_artist on Artist {
|
|
209
|
+
biography
|
|
210
|
+
}
|
|
211
|
+
`,
|
|
212
|
+
props.artist,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
return <div>About the artist: {props.artist.biography}</div>;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
</TabItem>
|
|
220
|
+
|
|
221
|
+
<TabItem value="TypeScript">
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
/**
|
|
225
|
+
* export type ExampleFragmentComponent_artist$data = {
|
|
226
|
+
* readonly name: string
|
|
227
|
+
* }
|
|
228
|
+
*
|
|
229
|
+
* export type ExampleFragmentComponent_artist$key = { ... }
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
import { ExampleFragmentComponent_artist$key } from "__generated__/ExampleFragmentComponent_artist.graphql"
|
|
233
|
+
|
|
234
|
+
interface Props {
|
|
235
|
+
artist: ExampleFragmentComponent_artist$key,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export default ExampleFragmentComponent(props: Props) {
|
|
239
|
+
// data is of type ExampleFragmentComponent_artist$data
|
|
240
|
+
const data = useFragment(
|
|
241
|
+
graphql`
|
|
242
|
+
fragment ExampleFragmentComponent_artist on Artist {
|
|
243
|
+
biography
|
|
244
|
+
}
|
|
245
|
+
`,
|
|
246
|
+
props.artist,
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return <div>About the artist: {props.artist.biography}</div>;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
</TabItem>
|
|
254
|
+
</Tabs>
|
|
255
|
+
|
|
256
|
+
## Fragment references
|
|
257
|
+
|
|
258
|
+
The opaque identifier described in [data-masking] that a child container expects to receive from its parent, which represents the child container’s fragment spread inside the parent’s fragment.
|
|
259
|
+
|
|
260
|
+
<OssOnly>
|
|
261
|
+
|
|
262
|
+
:::important
|
|
263
|
+
Please read [this important caveat](#single-artifact-directory) about actually enabling type-safe fragment reference checking.
|
|
264
|
+
:::
|
|
265
|
+
|
|
266
|
+
</OssOnly>
|
|
267
|
+
|
|
268
|
+
Consider a component that [composes](../../guided-tour/rendering/fragments/#composing-fragments) the above fragment component example. In this example, the emitted type-information of the child component receives a unique opaque identifier type, called a fragment reference, which the type-information emitted for the parent’s fragment references in the location where the child’s fragment is spread. Thus ensuring that the child’s fragment is spread into the parent’s fragment _and_ the correct fragment reference is passed to the child component at runtime.
|
|
269
|
+
|
|
270
|
+
<Tabs
|
|
271
|
+
defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
|
|
272
|
+
values={[
|
|
273
|
+
{label: 'Flow', value: 'Flow'},
|
|
274
|
+
{label: 'TypeScript', value: 'TypeScript'},
|
|
275
|
+
]}>
|
|
276
|
+
<TabItem value="Flow">
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
import { ExampleFragmentComponent } from "./ExampleFragmentComponent"
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* import type { ExampleFragmentComponent_artist$fragmentType } from "ExampleFragmentComponent_artist.graphql";
|
|
283
|
+
*
|
|
284
|
+
* export type ExampleQuery$data = {
|
|
285
|
+
* +artist: ?{
|
|
286
|
+
* +name: ?string,
|
|
287
|
+
* +$fragmentSpreads: ExampleFragmentComponent_artist$fragmentType,
|
|
288
|
+
* }
|
|
289
|
+
* };
|
|
290
|
+
* export type ExampleQuery$variables = {
|
|
291
|
+
* +artistID: string,
|
|
292
|
+
* }
|
|
293
|
+
* export type ExampleQuery = {
|
|
294
|
+
* +variables: ExampleQuery$variables,
|
|
295
|
+
* +response: ExampleQuery$data,
|
|
296
|
+
* }
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
// data is of type ExampleQuery$data
|
|
300
|
+
const data = useLazyLoadQuery(
|
|
301
|
+
graphql`
|
|
302
|
+
query ExampleQuery($artistID: ID!) {
|
|
303
|
+
artist(id: $artistID) {
|
|
304
|
+
name
|
|
305
|
+
...ExampleFragmentComponent_artist
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
`,
|
|
309
|
+
{artistID: 'banksy'},
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// Here only `data.artist.name` is directly visible,
|
|
313
|
+
// the marker prop $fragmentSpreads indicates that `data.artist`
|
|
314
|
+
// can be used for the component expecting this fragment spread.
|
|
315
|
+
return <ExampleFragmentComponent artist={data.artist} />;
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
</TabItem>
|
|
319
|
+
|
|
320
|
+
<TabItem value="TypeScript">
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
import { ExampleFragmentComponent } from "./ExampleFragmentComponent"
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* import { ExampleFragmentComponent_artist$fragmentType } from "ExampleFragmentComponent_artist.graphql";
|
|
327
|
+
*
|
|
328
|
+
* export type ExampleQuery$data = {
|
|
329
|
+
* readonly artist?: {
|
|
330
|
+
* readonly name: ?string,
|
|
331
|
+
* readonly " $fragmentSpreads": ExampleFragmentComponent_artist$fragmentType
|
|
332
|
+
* }
|
|
333
|
+
* }
|
|
334
|
+
* export type ExampleQuery$variables = {
|
|
335
|
+
* readonly artistID: string
|
|
336
|
+
* }
|
|
337
|
+
* export type ExampleQuery = {
|
|
338
|
+
* readonly variables: ExampleQuery$variables
|
|
339
|
+
* readonly response: ExampleQuery$data
|
|
340
|
+
* }
|
|
341
|
+
*/
|
|
342
|
+
|
|
343
|
+
// data is of type ExampleQuery$data
|
|
344
|
+
const data = useLazyLoadQuery(
|
|
345
|
+
graphql`
|
|
346
|
+
query ExampleQuery($artistID: ID!) {
|
|
347
|
+
artist(id: $artistID) {
|
|
348
|
+
name
|
|
349
|
+
...ExampleFragmentComponent_artist
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
`,
|
|
353
|
+
{artistID: 'banksy'},
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
// Here only `data.artist.name` is directly visible,
|
|
357
|
+
// the marker prop $fragmentSpreads indicates that `data.artist`
|
|
358
|
+
// can be used for the component expecting this fragment spread.
|
|
359
|
+
return <ExampleFragmentComponent artist={data.artist} />;
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
</TabItem>
|
|
363
|
+
</Tabs>
|
|
364
|
+
|
|
365
|
+
<OssOnly>
|
|
366
|
+
|
|
367
|
+
## Single artifact directory
|
|
368
|
+
|
|
369
|
+
An important caveat to note is that by default strict fragment reference type-information will _not_ be emitted, instead they will be typed as `any` and would allow you to pass in any data to the child component.
|
|
370
|
+
|
|
371
|
+
To enable this feature, you will have to tell the compiler to store all the artifacts in a single directory, by specifying the `artifactDirectory` in the
|
|
372
|
+
compiler configuration:
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
{
|
|
376
|
+
// package.json
|
|
377
|
+
"relay": {
|
|
378
|
+
"artifactDirectory": "./src/__generated__",
|
|
379
|
+
...
|
|
380
|
+
},
|
|
381
|
+
...
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
…and additionally inform the babel plugin in your `.babelrc` config where to look for the artifacts:
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"plugins": [
|
|
390
|
+
["relay", { "artifactDirectory": "./src/__generated__" }]
|
|
391
|
+
]
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
It is recommended to alias this directory in your module resolution configuration such that you don’t need to specify relative paths in your source files. This is what is also done in the above examples, where artifacts are imported from a `__generated__` alias, rather than relative paths like `../../../../__generated__`.
|
|
396
|
+
|
|
397
|
+
### Background information
|
|
398
|
+
|
|
399
|
+
The reason is that `relay-compiler` and its artifact emission is stateless. Meaning that it does not keep track of locations of original source files and where the compiler previously saved the accompanying artifact on disk. Thus, if the compiler were to emit artifacts that try to import fragment reference types from _other_ artifacts, the compiler would:
|
|
400
|
+
|
|
401
|
+
- first need to know where on disk that other artifact exists;
|
|
402
|
+
- and update imports when the other artifact changes location on disk.
|
|
403
|
+
|
|
404
|
+
Facebook uses a module system called [Haste], in which all source files are considered in a flat namespace. This means that an import declaration does not need to specify the path to another module and thus there is no need for the compiler to ever consider the above issues. I.e. an import only needs to specify the basename of the module filename and Haste takes care of actually finding the right module at import time. Outside of Facebook, however, usage of the Haste module system is non-existent nor encouraged, thus the decision to not import fragment reference types but instead type them as `any`.
|
|
405
|
+
|
|
406
|
+
At its simplest, we can consider Haste as a single directory that contains all module files, thus all module imports always being safe to import using relative sibling paths. This is what is achieved by the single artifact directory feature. Rather than co-locating artifacts with their source files, all artifacts are stored in a single directory, allowing the compiler to emit imports of fragment reference types.
|
|
407
|
+
|
|
408
|
+
</OssOnly>
|
|
409
|
+
|
|
410
|
+
[data-masking]: ../../principles-and-architecture/thinking-in-relay#data-masking
|
|
411
|
+
|
|
412
|
+
[Haste]: https://twitter.com/dan_abramov/status/758655309212704768
|
|
413
|
+
|
|
414
|
+
<DocsRating />
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: home
|
|
3
|
+
title: Home
|
|
4
|
+
slug: /
|
|
5
|
+
description: Relay documentation landing page
|
|
6
|
+
keywords:
|
|
7
|
+
- relay
|
|
8
|
+
- graphql
|
|
9
|
+
- data
|
|
10
|
+
- introduction
|
|
11
|
+
- home
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Relay Docs
|
|
15
|
+
|
|
16
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
17
|
+
import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
|
|
18
|
+
|
|
19
|
+
Relay is a powerful [GraphQL](https://graphql.org/) client for [React](https://react.dev/). It embodies years of learning to give you **outstanding performance by default** while keeping your code **scalable and maintainable**.
|
|
20
|
+
|
|
21
|
+
Relay brings the composability of React components to data fetching. Each component declares its own data needs, and Relay combines them into efficient pre-loadable queries. Every aspect of its design is to make the natural way of writing components also the most performant.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
* **Declarative**: Just declare what data each component needs and Relay will handle generating [optimal queries](https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/) for each surface.
|
|
26
|
+
* **Composable**: Components act like building bricks that can click into place anywhere in your app without needing to manually update queries.
|
|
27
|
+
* **Pre-fetchable**: Relay's generated queries allow you to start fetching data for your surface before your code even downloads or runs.
|
|
28
|
+
* **Built-in UI patterns**: Relay implements loading states, pagination, refetching, optimistic updates, rollbacks, and other common UI behaviors that are tricky to get right.
|
|
29
|
+
* **Consistent state**: Relay maintains a normalized data store, so components that observe the same data stay in sync even if they reach it by different queries.
|
|
30
|
+
* **Type safe**: Relay generates TypeScript types for each GraphQL snippet so that errors are caught statically, not at runtime.
|
|
31
|
+
* **Streaming/deferred data**: Declaratively defer parts of your query and Relay will progressively re-render your UI as the data streams in.
|
|
32
|
+
* **Developer experience**: Relay's [editor support](./editor-support.mdx) provides autocompletion and go-to-definition for your GraphQL schema.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: architecture-overview
|
|
3
|
+
title: Architecture Overview
|
|
4
|
+
slug: /principles-and-architecture/architecture-overview/
|
|
5
|
+
description: Relay architecture overview guide
|
|
6
|
+
keywords:
|
|
7
|
+
- architecture
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
11
|
+
|
|
12
|
+
This document, together with [Runtime Architecture](../runtime-architecture/) and [Compiler Architecture](../compiler-architecture/), describes the high-level architecture of Relay. The intended audience includes developers interested in contributing to Relay, developers hoping to utilize the building blocks of Relay to create higher-level APIs, and anyone interested in understanding more about Relay internals. For developers wanting to learn more about _using_ Relay to build products, the [Tutorial](../tutorial/intro.mdx) is the best resource.
|
|
13
|
+
|
|
14
|
+
## Core Modules
|
|
15
|
+
|
|
16
|
+
Relay is composed of three core parts:
|
|
17
|
+
|
|
18
|
+
- **Relay Compiler:** A GraphQL to GraphQL optimizing _compiler_, providing general utilities for transforming and optimizing queries as well as generating build artifacts. A novel feature of the compiler is that it facilitates experimentation with new GraphQL features - in the form of custom directives - by making it easy to translate code using these directives into standard, spec-compliant GraphQL.
|
|
19
|
+
- **Relay Runtime:** A full-featured, high-performance GraphQL _runtime_ that can be used to build higher-level client APIs. The runtime features a normalized object cache, optimized "write" and "read" operations, a generic abstraction for incrementally fetching field data (such as for pagination), garbage collection for removing unreferenced cache entries, optimistic mutations with arbitrary logic, support for building subscriptions and live queries, and more.
|
|
20
|
+
- **React/Relay:** A high-level _product API_ that integrates the Relay Runtime with React. This is the primary public interface to Relay for most product developers, featuring APIs to fetch the data for a query or define data dependencies for reusable components (e.g. `useFragment`).
|
|
21
|
+
|
|
22
|
+
Note that these modules are _loosely coupled_. For example, the compiler emits representations of queries in a well-defined format that the runtime consumes, such that the compiler implementation can be swapped out if desired. React/Relay relies only on the well-documented public interface of the runtime, such that the actual implementation can be swapped out. We hope that this loose coupling will allow the community to explore new use-cases such as the development of specialized product APIs using the Relay runtime or integrations of the runtime with view libraries other than React.
|
|
23
|
+
|
|
24
|
+
<DocsRating />
|