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,664 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: server-3d
|
|
3
|
+
title: Server 3D
|
|
4
|
+
slug: /guides/data-driven-dependencies/server-3d/
|
|
5
|
+
description: Server side data driven dependencies (3D)
|
|
6
|
+
keywords:
|
|
7
|
+
- 3D
|
|
8
|
+
- Server 3D
|
|
9
|
+
- data driven dependencies
|
|
10
|
+
- module
|
|
11
|
+
- match
|
|
12
|
+
- MatchContainer
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
import DocsRating from '@site/src/core/DocsRating';
|
|
16
|
+
import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
|
|
17
|
+
// @fb-only
|
|
18
|
+
|
|
19
|
+
<FbInternalOnly>
|
|
20
|
+
|
|
21
|
+
:::note
|
|
22
|
+
Throughout this guide, we use `MatchContainer`. If you are in www, but not in Comet, you should use RelayFBMatchContainer.
|
|
23
|
+
:::
|
|
24
|
+
|
|
25
|
+
</FbInternalOnly>
|
|
26
|
+
|
|
27
|
+
<OssOnly>
|
|
28
|
+
|
|
29
|
+
:::warning
|
|
30
|
+
Server 3D requires configuring your server to support various features! It is unlikely to work in OSS without significant work. Relay does not claim to fully support Server 3D in OSS (yet), but [Client 3D](../client-3d/) is fully supported.
|
|
31
|
+
:::
|
|
32
|
+
|
|
33
|
+
</OssOnly>
|
|
34
|
+
|
|
35
|
+
Use server 3D when all the data fields used to render your 3D components are fetched from GraphQL servers.
|
|
36
|
+
|
|
37
|
+
## Simple 3D with @module
|
|
38
|
+
|
|
39
|
+
The basic cases for 3D are the first two cases described in [Use Cases](../introduction/#use-cases): content that is typically missing (where the corresponding rendering code is typically unused) or a union of many types (where only some of the possible rendering code is typically used). These cases are supported with the `@module(name: String)` directive on fragment spreads, which specifies a React component to download only if the data exists and fragment's type matches.
|
|
40
|
+
|
|
41
|
+
### @module Usage Guide
|
|
42
|
+
|
|
43
|
+
Let's walk through how to handle a comment that may contain an image attachment, where we only want to download the image rendering code when an image is actually present.
|
|
44
|
+
|
|
45
|
+
#### Server Changes
|
|
46
|
+
|
|
47
|
+
<FbInternalOnly>
|
|
48
|
+
|
|
49
|
+
* For each concrete (GraphQLObject) type that you want to use `@module` with, update the schema to use the `HasJSDependency` trait. In this case we'd add the trait to the type of the `Comment.image` field — let's say that is `CommentImage`:
|
|
50
|
+
|
|
51
|
+
```php
|
|
52
|
+
<<
|
|
53
|
+
GraphQLObject('CommentImage', 'An image attached to a comment'),
|
|
54
|
+
Oncalls('<todo>')
|
|
55
|
+
>>
|
|
56
|
+
final class CommentImage ... {
|
|
57
|
+
// Note: Each type used with @module must use the `HasJSDependency` trait
|
|
58
|
+
// to allow dynamically loading a client-specified React component
|
|
59
|
+
use HasJSDependency;
|
|
60
|
+
|
|
61
|
+
...
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
* Rebuild the GraphQL schema with `phps graphql`.
|
|
65
|
+
|
|
66
|
+
</FbInternalOnly>
|
|
67
|
+
|
|
68
|
+
<OssOnly>
|
|
69
|
+
|
|
70
|
+
* For each concrete (GraphQLObject) type that you want to use `@module` with, update the schema to support the fields (`__fragment` and `__component`) that are present later in this document.
|
|
71
|
+
|
|
72
|
+
</OssOnly>
|
|
73
|
+
|
|
74
|
+
#### Client Changes
|
|
75
|
+
|
|
76
|
+
Your Relay fragment can now use `@module`. In this example, if the `comment.image` field is present (non-null), we load the `CommentImage.react` component and use the `CommentImage_image` fragment to load its data.
|
|
77
|
+
|
|
78
|
+
On the Relay side you'd write:
|
|
79
|
+
|
|
80
|
+
```graphql
|
|
81
|
+
fragment Comment_comment on Comment {
|
|
82
|
+
image {
|
|
83
|
+
...CommentImage_image
|
|
84
|
+
@module(name: "CommentImage.react")
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
<FbInternalOnly>
|
|
90
|
+
Which the server receives as the following:
|
|
91
|
+
|
|
92
|
+
```graphql
|
|
93
|
+
fragment Comment_comment on Comment {
|
|
94
|
+
image {
|
|
95
|
+
... on CommentImage {
|
|
96
|
+
...CommentImage_image
|
|
97
|
+
__component: js("CommentImage.react")
|
|
98
|
+
__fragment: js("CommentImage_image$normalization.graphql")
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
</FbInternalOnly>
|
|
104
|
+
|
|
105
|
+
To consume the `comment.image` field and render the component when the data exists, you shouldn't statically require the component (which would introduce a static dependency) and instead use `MatchContainer` to return the dynamically selected component:
|
|
106
|
+
|
|
107
|
+
<FbInternalOnly>
|
|
108
|
+
Within Meta, use `CometRelay.MatchContainer` (Comet) or `RelayFBMatchContainer` (www).
|
|
109
|
+
</FbInternalOnly>
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const {useFragment, graphql, MatchContainer} = require('react-relay');
|
|
113
|
+
|
|
114
|
+
function CommentRenderer(props) {
|
|
115
|
+
const comment = useFragment(
|
|
116
|
+
/* fragment Comment_comment from above */,
|
|
117
|
+
props.comment,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (comment.image == null) {
|
|
121
|
+
// Handle cases where the field failed to load or was null
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
// MatchContainer may suspend while loading the component or its data,
|
|
125
|
+
// consider wrapping with React.Suspense.
|
|
126
|
+
return (
|
|
127
|
+
<Suspense fallback={null}>
|
|
128
|
+
<MatchContainer
|
|
129
|
+
// data for field containing @module selection
|
|
130
|
+
match={comment.image}
|
|
131
|
+
props={{ /* ...other props... */ }}
|
|
132
|
+
/>
|
|
133
|
+
</Suspense>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
module.exports = CommentRenderer;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
:::caution
|
|
140
|
+
When using MatchContainer, the component loaded using 3D needs to have the same prop name as the fragment suffix e.g. if your fragment is `Comment_comment`, your prop needs to be called `comment` instead of something like `comment$key`
|
|
141
|
+
:::
|
|
142
|
+
|
|
143
|
+
## Advanced 3D with @match
|
|
144
|
+
|
|
145
|
+
In some cases a given piece of content can be rendered in a variety of different rendering strategies. In this case, the client and server have to negotiate to choose the ideal strategy for each piece of content: the content may be eligible to be rendered as SuperFancyMarkdown, but if the client doesn't support that type the app should fallback to just regular Markdown rather than showing nothing at all. Relay supports this client/server negotiation with the `@match` directive.
|
|
146
|
+
|
|
147
|
+
### @match Design Principles
|
|
148
|
+
|
|
149
|
+
* The client specifies which strategies it supports (a given client may not support all possible strategies), how it will render that data (one React component per strategy), and what data it needs (a GraphQL fragment for each strategy, describing the React component's data dependencies).
|
|
150
|
+
* The server - specifically *product logic in the schema* - selects the rendering strategy to use, selecting the "best" strategy given the user, data, and the client's supported strategies.
|
|
151
|
+
* The code (Component) and data (GraphQL) for the selected strategy is downloaded *dynamically* once the strategy is selected. Data is downloaded as normal GraphQL data, and metadata about the code is sent down in a side-channel (technically, in the [`extensions`](https://graphql.org/learn/response/#extensions) field of the GraphQL payload).
|
|
152
|
+
|
|
153
|
+
### @match Usage Guide
|
|
154
|
+
|
|
155
|
+
Let's walk through the steps to implementing an example of adding a new data-driven dependency for a `Comment` type with `markdown` and `plaintext` rendering strategies.
|
|
156
|
+
|
|
157
|
+
#### Server Changes
|
|
158
|
+
|
|
159
|
+
<FbInternalOnly>
|
|
160
|
+
|
|
161
|
+
* Define a new `GraphQLUnion` type with a variant for each rendering strategy:
|
|
162
|
+
|
|
163
|
+
```php
|
|
164
|
+
<<
|
|
165
|
+
GraphQLUnion('CommentRenderer', 'Data-driven dependency for comments...'),
|
|
166
|
+
Oncalls('<todo>')
|
|
167
|
+
>>
|
|
168
|
+
interface ICommentRenderer extends IGraphQLUnion {}
|
|
169
|
+
|
|
170
|
+
<<
|
|
171
|
+
GraphQLObject('CommentMarkdownRenderer', 'Comment with markdown rendering'),
|
|
172
|
+
Oncalls('<todo>')
|
|
173
|
+
>>
|
|
174
|
+
final class CommentMarkdownRenderer implements ICommentRenderer {
|
|
175
|
+
// Note: Each class in the union must use the `HasJSDependency` trait to allow
|
|
176
|
+
// dynamically loading a client-specified React compon
|
|
177
|
+
use HasJSDependency;
|
|
178
|
+
|
|
179
|
+
<<GraphQLField('markdown', 'Markdown text')>>
|
|
180
|
+
public function markdown(): string {
|
|
181
|
+
return 'markdown'; // todo: load markdown content from the comment
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
<<
|
|
186
|
+
GraphQLObject('CommentPlaintextRenderer', 'Comment with plaintext rendering'),
|
|
187
|
+
Oncalls('<todo>')
|
|
188
|
+
>>
|
|
189
|
+
final class CommentPlaintextRenderer implements ICommentRenderer {
|
|
190
|
+
// Note: Each class in the union must use the `HasJSDependency` trait to allow
|
|
191
|
+
// dynamically loading a client-specified React compon
|
|
192
|
+
use HasJSDependency;
|
|
193
|
+
|
|
194
|
+
<<GraphQLField('plaintext', 'Plaintext')>>
|
|
195
|
+
public function plaintext(): string {
|
|
196
|
+
return 'plaintext'; // todo: load text content from the comment
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
* Add a new field on the `Comment` type that accepts a `Traversable<string> supported` argument listing the client's supported strategies, and returns one of the union values to indicate the selected strategy:
|
|
201
|
+
|
|
202
|
+
```php
|
|
203
|
+
<<
|
|
204
|
+
GraphQLObject('Comment', 'Comment on a post'),
|
|
205
|
+
Oncalls('<todo>')
|
|
206
|
+
>>
|
|
207
|
+
final class Comment {
|
|
208
|
+
<<GraphQLField(
|
|
209
|
+
'comment_content_renderer',
|
|
210
|
+
'Field that returns a rendering strategy for the main content of the comment',
|
|
211
|
+
)>>
|
|
212
|
+
public function commentContentRenderer(Traversable<string> $supported): ICommentRenderer {
|
|
213
|
+
if (C\contains($supported, 'CommentMarkdownRenderer') && userIsEligibleForMarkdownContent()) {
|
|
214
|
+
return new CommentMarkdownRenderer();
|
|
215
|
+
}
|
|
216
|
+
return new CommentPlaintextRenderer();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
* Rebuild the GraphQL schema with `arc rebuild`.
|
|
221
|
+
|
|
222
|
+
#### Choose Render Strategy API
|
|
223
|
+
|
|
224
|
+
In some cases, your use case might require multiple and more complex checks in order to choose a proper render strategy. For those cases, you can extend your strategies with `IRenderStrategy`. So, the example above could be extended aggregating `CommentMarkupRenderer` simply writing the following code:
|
|
225
|
+
|
|
226
|
+
```php
|
|
227
|
+
<<
|
|
228
|
+
GraphQLObject('CommentMarkupRenderer', 'Comment with markup rendering'),
|
|
229
|
+
Oncalls('<todo>')
|
|
230
|
+
>>
|
|
231
|
+
final class CommentMarkupRenderer implements ICommentRenderer, IRenderStrategy {
|
|
232
|
+
|
|
233
|
+
use HasJSDependency;
|
|
234
|
+
|
|
235
|
+
<<GraphQLField('markup', 'Markdown text')>>
|
|
236
|
+
public function markdown(): string {
|
|
237
|
+
return 'markup'; // todo: load markup content from the comment
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
<<__Override>>
|
|
241
|
+
public async function genShouldShow(): Awaitable<bool> {
|
|
242
|
+
return userIsEligibleForMarkupContent();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
final class CommentMarkdownRenderer implements ICommentRenderer, IRenderStrategy {
|
|
246
|
+
// ...
|
|
247
|
+
<<__Override>>
|
|
248
|
+
public async function genShouldShow(): Awaitable<bool> {
|
|
249
|
+
return userIsEligibleForMarkdownContent();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
final class CommentPlaintextRenderer implements ICommentRenderer, IRenderStrategy {
|
|
254
|
+
// ...
|
|
255
|
+
<<__Override>>
|
|
256
|
+
public async function genShouldShow(): Awaitable<bool> {
|
|
257
|
+
// By default we want to use this strategy as a fallback
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
having all RendererStrategies implementing IRenderStrategy, we can choose the strategy using `RenderStrategySelector::genChooseStrategy`. Making sure to place strategies in the proper order, since the first strategy that `genShouldShow` returns true will be picked as:
|
|
263
|
+
|
|
264
|
+
```php
|
|
265
|
+
public function commentContentRenderer(Traversable<string> $supported): Awaitable<ICommentRenderer> {
|
|
266
|
+
return await RenderStrategySelector::genChooseStrategy(
|
|
267
|
+
vec[
|
|
268
|
+
CommentMarkdownRenderer(),
|
|
269
|
+
CommentMarkupRenderer(),
|
|
270
|
+
CommentPlaintextRenderer(),
|
|
271
|
+
],
|
|
272
|
+
$supported,
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
</FbInternalOnly>
|
|
278
|
+
|
|
279
|
+
<OssOnly>
|
|
280
|
+
|
|
281
|
+
* Define a new `GraphQLUnion` type with a variant for each rendering strategy.
|
|
282
|
+
* Add a new field on the `Comment` type that accepts an `Array<string> supported` argument listing the client's supported strategies, and returns one of the union values to indicate the selected strategy.
|
|
283
|
+
|
|
284
|
+
For example:
|
|
285
|
+
```graphql
|
|
286
|
+
type CommentMarkdownRenderer {
|
|
287
|
+
markdown_content: String
|
|
288
|
+
__component: JSDependency
|
|
289
|
+
__fragment: JSDependency
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
type CommentPlaintextRenderer {
|
|
293
|
+
plaintext_content: String
|
|
294
|
+
__component: JSDependency
|
|
295
|
+
__fragment: JSDependency
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
union CommentRenderer = CommentMarkdownRenderer | CommentPlaintextRenderer
|
|
299
|
+
|
|
300
|
+
type Comment {
|
|
301
|
+
# ... other fields here
|
|
302
|
+
comment_content_renderer(supported: [String!]): CommentRenderer
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
</OssOnly>
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
#### Client Changes
|
|
310
|
+
|
|
311
|
+
Your Relay fragment can now use `@match` to specify that for the `comment_content_renderer` field, we expect dependencies to be decided by the data. In this example, if the `comment_content_renderer` field is of type `CommentMarkdownRenderer`, we load the `CommentMarkdownRenderer.react` component and use the `CommentMarkdownRenderer_comment` fragment to load its data. Similar for the plaintext variant.
|
|
312
|
+
|
|
313
|
+
:::caution
|
|
314
|
+
The inline fragments annotated with `@module` on the same parent 3D fragment must be on distinct concrete types. If they are on the same concrete type, the relay compiler will report an error. So in the example below, `CommentMarkdownRenderer_comment` must be on a different concrete type than `CommentPlaintextRenderer_comment` (for example, the former could be on a `MarkdownComment` type, and the latter on a `PlaintextComment` type. Both could implement a parent interface `Comment`).
|
|
315
|
+
:::
|
|
316
|
+
|
|
317
|
+
On the Relay side you'd write:
|
|
318
|
+
|
|
319
|
+
```graphql
|
|
320
|
+
fragment Comment_comment on Comment {
|
|
321
|
+
comment_content_renderer @match {
|
|
322
|
+
...CommentMarkdownRenderer_comment
|
|
323
|
+
@module(name: "CommentMarkdownRenderer.react")
|
|
324
|
+
|
|
325
|
+
...CommentPlaintextRenderer_comment
|
|
326
|
+
@module(name: "CommentPlaintextRenderer.react")
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
Which the server receives as the following - note that the `supported` argument is generated automatically based on the types that we have provided fragments for above:
|
|
331
|
+
|
|
332
|
+
<FbInternalOnly>
|
|
333
|
+
|
|
334
|
+
```graphql
|
|
335
|
+
fragment Comment_comment on Comment {
|
|
336
|
+
comment_content_renderer(supported: ["CommentMarkdownRenderer", "CommentPlaintextRenderer"]) {
|
|
337
|
+
... on CommentMarkdownRenderer {
|
|
338
|
+
...CommentMarkdownRenderer_comment
|
|
339
|
+
__component: js("CommentMarkdownRenderer.react")
|
|
340
|
+
__fragment: js("CommentMarkdownRenderer_comment$normalization.graphql")
|
|
341
|
+
}
|
|
342
|
+
... on CommentPlaintextRenderer {
|
|
343
|
+
...CommentPlaintextRenderer_comment
|
|
344
|
+
__component: js("CommentPlaintextRenderer.react")
|
|
345
|
+
__fragment: js("CommentPlaintextRenderer_comment$normalization.graphql")
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
</FbInternalOnly>
|
|
352
|
+
|
|
353
|
+
<OssOnly>
|
|
354
|
+
|
|
355
|
+
```graphql
|
|
356
|
+
fragment Comment_comment on Comment {
|
|
357
|
+
comment_content_renderer(supported: ["CommentMarkdownRenderer", "CommentPlaintextRenderer"]) {
|
|
358
|
+
... on CommentMarkdownRenderer {
|
|
359
|
+
...CommentMarkdownRenderer_comment
|
|
360
|
+
}
|
|
361
|
+
... on CommentPlaintextRenderer {
|
|
362
|
+
...CommentPlaintextRenderer_comment
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
</OssOnly>
|
|
369
|
+
|
|
370
|
+
To consume the comment_content_renderer field and render the appropriate container, you shouldn't statically require the component (which would introduce a static dependency) and instead use `MatchContainer` to return the dynamically selected component:
|
|
371
|
+
|
|
372
|
+
```js
|
|
373
|
+
const React = require('React');
|
|
374
|
+
const {Suspense} = React;
|
|
375
|
+
const {graphql, useFragment, MatchContainer} = require('react-relay');
|
|
376
|
+
|
|
377
|
+
function CommentRenderer(props) {
|
|
378
|
+
const comment = useFragment(
|
|
379
|
+
/* fragment from above */,
|
|
380
|
+
props.comment,
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
if (comment.comment_content_renderer == null) {
|
|
384
|
+
// Handle cases where the field failed to load or was null
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// MatchContainer may suspend while loading the component/its data,
|
|
389
|
+
// consider wrapping with React.Suspense.
|
|
390
|
+
return (
|
|
391
|
+
<Suspense fallback={null}>
|
|
392
|
+
<MatchContainer
|
|
393
|
+
// data for field containing at-module selection
|
|
394
|
+
match={comment.comment_content_renderer}
|
|
395
|
+
props={{/* other props */}}
|
|
396
|
+
/>
|
|
397
|
+
</Suspense>
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
module.exports = CommentRenderer;
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
:::caution
|
|
404
|
+
When using MatchContainer, the component loaded using 3D needs to have the same prop name as the fragment suffix e.g. if your fragment is `Comment_comment`, your prop needs to be called `comment` instead of something like `comment$key`
|
|
405
|
+
:::
|
|
406
|
+
|
|
407
|
+
## Multiple 3D Selections Per Fragment
|
|
408
|
+
|
|
409
|
+
If your component needs to select multiple data-driven dependencies in a single fragment, each field must be named with a distinct `key`. The key can be provided by adding the `@match` directive on the parent field:
|
|
410
|
+
|
|
411
|
+
```graphql
|
|
412
|
+
# DOESN'T WORK
|
|
413
|
+
fragment Example_comment on Comment {
|
|
414
|
+
comment_content_renderer @match {
|
|
415
|
+
...CommentMarkdownRenderer_comment
|
|
416
|
+
@module(name: "CommentMarkdownRenderer.react")
|
|
417
|
+
}
|
|
418
|
+
attachments {
|
|
419
|
+
attachment_renderer {
|
|
420
|
+
...CommentAttachmentPhotoRenderer_comment
|
|
421
|
+
@module(name: "CommentPhotoRenderer.react")
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
This will fail with a message such as:
|
|
428
|
+
|
|
429
|
+
```
|
|
430
|
+
`Error: Invalid @module selection: documents with multiple fields containing 3D
|
|
431
|
+
selections must specify a unique 'key' value for each field:
|
|
432
|
+
use 'attachment_renderer @match(key: "ExampleComment_<localName>")'.`
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
In this case, follow the suggestion in the error and add `@match(key: "...")` on the second 3D field (`attachment_renderer` in this case):
|
|
436
|
+
|
|
437
|
+
```
|
|
438
|
+
// OK - different keys with @match
|
|
439
|
+
fragment Example_comment on Comment {
|
|
440
|
+
comment_content_renderer @match {
|
|
441
|
+
...CommentMarkdownRenderer_comment
|
|
442
|
+
@module(name: "CommentMarkdownRenderer.react")
|
|
443
|
+
}
|
|
444
|
+
attachments {
|
|
445
|
+
attachment_renderer @match(key: "Example_comment__attachment") {
|
|
446
|
+
...CommentAttachmentPhotoRenderer_comment
|
|
447
|
+
@module(name: "CommentPhotoRenderer.react")
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Internally, Relay uses the 'key' value to isolate the results of each field in the store. This ensures that even if both fields return the same object, that the results can't collide.
|
|
454
|
+
|
|
455
|
+
## Usage with Relay Hooks
|
|
456
|
+
|
|
457
|
+
The preferred way of using 3D is with the [`useFragment`](../../../api-reference/use-fragment/) API.
|
|
458
|
+
|
|
459
|
+
```js
|
|
460
|
+
// CommentRenderer.react.js
|
|
461
|
+
|
|
462
|
+
const {graphql, useFragment, MatchContainer} = require('react-relay');
|
|
463
|
+
|
|
464
|
+
function CommentRenderer(props) {
|
|
465
|
+
const comment = useFragment(
|
|
466
|
+
graphql`
|
|
467
|
+
fragment Comment_comment on Comment {
|
|
468
|
+
image {
|
|
469
|
+
...CommentImageRenderer_image @module(name: "CommentImageRenderer.react")
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
`,
|
|
473
|
+
props.comment,
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
if (comment.image == null) {
|
|
477
|
+
// Handle cases where the field failed to load or was null
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// MatchContainer may suspend while loading the component/its data,
|
|
482
|
+
// consider wrapping with React.Suspense.
|
|
483
|
+
return (
|
|
484
|
+
<Suspense fallback={null}>
|
|
485
|
+
<MatchContainer
|
|
486
|
+
// data for field containing @module selection
|
|
487
|
+
match={comment.image}
|
|
488
|
+
props={{...other props...}}
|
|
489
|
+
/>
|
|
490
|
+
</Suspense>
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
module.exports = CommentRenderer;
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
// @fb-only
|
|
497
|
+
|
|
498
|
+
The component that is dynamically loaded via 3D can also be a component that uses `useFragment`:
|
|
499
|
+
|
|
500
|
+
```js
|
|
501
|
+
// CommentImageRenderer.react.js
|
|
502
|
+
import type {CommentImageRenderer_image$key} from 'CommentImageRenderer_image.graphql'
|
|
503
|
+
|
|
504
|
+
const {useFragment} = require('react-relay');
|
|
505
|
+
|
|
506
|
+
type Props = {
|
|
507
|
+
image: CommentImageRenderer_image$key,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
function CommentImageRenderer(props) {
|
|
511
|
+
const data = useFragment(
|
|
512
|
+
graphql`
|
|
513
|
+
fragment CommentImageRenderer_image on Image {
|
|
514
|
+
src
|
|
515
|
+
}
|
|
516
|
+
`,
|
|
517
|
+
props.image,
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
return (...);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
module.exports = CommentImageRenderer;
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
<FbInternalOnly>
|
|
527
|
+
|
|
528
|
+
## Using non-React modules
|
|
529
|
+
|
|
530
|
+
The typical usage of data-driven dependencies is to dynamically load modules that export a React component with data-dependencies expressed via Relay. However, Relay also supports dynamically loading *arbitrary* JS modules. This works the same `@match` / `@module` syntax, but (as you may expect) `MatchContainer` won't work for this case. Instead, use `ModuleResource.read()`. The above example using `MatchContainer` can be rewritten to manually read and use the `@module` result:
|
|
531
|
+
|
|
532
|
+
:::tip
|
|
533
|
+
In www, outside of Comet, you should use `RelayFBModuleResource.read()` instead of `ModuleResource.read()`.
|
|
534
|
+
:::
|
|
535
|
+
|
|
536
|
+
```js
|
|
537
|
+
const React = require('React');
|
|
538
|
+
const {Suspense} = React;
|
|
539
|
+
const {graphql, useFragment, ModuleResource} = require('react-relay');
|
|
540
|
+
const CommentFragment = graphql`
|
|
541
|
+
fragment Comment_comment on Comment {
|
|
542
|
+
comment_content_renderer @match {
|
|
543
|
+
...CommentMarkdownRenderer_comment
|
|
544
|
+
@module(name: "CommentMarkdownRenderer.react")
|
|
545
|
+
...CommentPlaintextRenderer_comment
|
|
546
|
+
@module(name: "CommentPlaintextRenderer.react")
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
`;
|
|
550
|
+
function CommentRenderer(props) {
|
|
551
|
+
const comment = useFragment(
|
|
552
|
+
CommentFragment,
|
|
553
|
+
props.comment,
|
|
554
|
+
);
|
|
555
|
+
if (comment.image == null) {
|
|
556
|
+
// Handle cases where the field failed to load or was null
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
// NOTE: this will suspend if the module is not loaded:
|
|
560
|
+
// the *parent* component should wrap this one in a Suspense boundary
|
|
561
|
+
// MatchedModule will be:
|
|
562
|
+
// - null if there was no match
|
|
563
|
+
// - CommentMarkdownRenderer.react if the result was markdown
|
|
564
|
+
// - CommentPlaintextRenderer.react if the result was plaintext
|
|
565
|
+
|
|
566
|
+
const MatchedModule = ModuleResource.read(comment.image);
|
|
567
|
+
|
|
568
|
+
if (MatchedModule == null) {
|
|
569
|
+
return null; // no match
|
|
570
|
+
}
|
|
571
|
+
// Here we ensure that all possible matched components accept the data
|
|
572
|
+
// on the same prop key, in this case 'comment'
|
|
573
|
+
// Note that MatchContainer automatically determines the
|
|
574
|
+
// correct prop key to use for the matched data.
|
|
575
|
+
return (
|
|
576
|
+
<MatchedModule
|
|
577
|
+
comment={comment.image}
|
|
578
|
+
/>
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
module.exports = CommentRenderer;
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
You can also use `@module` directly to load a non-React module for a field if it isn't null (without using `@match`), and similarly consume the module using `ModuleResource.read()`:
|
|
585
|
+
|
|
586
|
+
:::tip
|
|
587
|
+
In www, outside of Comet, you should use `RelayFBModuleResource.read()` instead of `ModuleResource.read()`.
|
|
588
|
+
:::
|
|
589
|
+
|
|
590
|
+
```js
|
|
591
|
+
function CommentRenderer(props) {
|
|
592
|
+
const comment = useFragment(
|
|
593
|
+
graphql`
|
|
594
|
+
fragment Comment_comment on Comment {
|
|
595
|
+
image {
|
|
596
|
+
...CommentImage_image
|
|
597
|
+
@module(name: "ImageProcessingModule")
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
`,
|
|
601
|
+
props.comment,
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
if (comment.image == null) {
|
|
605
|
+
// Handle cases where the field failed to load or was null
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// NOTE: this will suspend if the module is not loaded
|
|
610
|
+
const ImageProcessingModule = ModuleResource.read(comment.image);
|
|
611
|
+
|
|
612
|
+
if (ImageProcessingModule == null) {
|
|
613
|
+
return null; // no match
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ...
|
|
617
|
+
}
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
**Note:** `@module` requires a fragment, which cannot be empty. If you don't want to fetch any data from the server (only conditionally files), you can define a "dummy" fragment for your field:
|
|
621
|
+
|
|
622
|
+
```javascript
|
|
623
|
+
// Define a fragment as a wrapper to use with @module
|
|
624
|
+
// The fragment below will be able to reference this fragment by name
|
|
625
|
+
graphql`
|
|
626
|
+
fragment FragmentForModule_image on Image {
|
|
627
|
+
__typename # only use __typename here since we don't need any data
|
|
628
|
+
}
|
|
629
|
+
`;
|
|
630
|
+
|
|
631
|
+
function CommentRenderer(props) {
|
|
632
|
+
const comment = useFragment(
|
|
633
|
+
graphql`
|
|
634
|
+
fragment Comment_comment on Comment {
|
|
635
|
+
image {
|
|
636
|
+
# Spread wrapper fragment
|
|
637
|
+
...FragmentForModule_image
|
|
638
|
+
@module(name: "ImageProcessingModule")
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
`,
|
|
642
|
+
props.comment,
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
// ...
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
</FbInternalOnly>
|
|
650
|
+
|
|
651
|
+
## Important Notes / Troubleshooting
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
* Note that `MatchContainer` will suspend until the selected component finishes loading, so be sure to wrap it in a `Suspense` placeholder.
|
|
655
|
+
|
|
656
|
+
<FbInternalOnly>
|
|
657
|
+
|
|
658
|
+
## ServerCallableModule Is No Longer Required
|
|
659
|
+
|
|
660
|
+
Usage of 3D **previously** required adding an `@ServerCallableModule` annotation to components loaded with `@module`. **This annotation is no longer required**. You may see diffs titled "[Codemod][DeadServerCallable]" that remove these now-unnecessary annotations, these diffs are expected and safe to land so long as they are only removing these annotations and not accidentally making other changes (i.e. please sanity-check the bot!).
|
|
661
|
+
|
|
662
|
+
</FbInternalOnly>
|
|
663
|
+
|
|
664
|
+
<DocsRating />
|