UNPKG

5.37 kBJavaScriptView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails oncall+relay
8 *
9 * @format
10 */
11'use strict';
12
13var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
14
15var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
16
17var React = require('react');
18
19var useMemo = React.useMemo;
20/**
21 * Renders the results of a data-driven dependency fetched with the `@match`
22 * directive. The `@match` directive can be used to specify a mapping of
23 * result types to the containers used to render those types. The result
24 * value is an opaque object that described which component was selected
25 * and a reference to its data. Use <MatchContainer/> to render these
26 * values.
27 *
28 * ## Example
29 *
30 * For example, consider a piece of media content that might be text or
31 * an image, where for clients that don't support images the application
32 * should fall back to rendering the image caption as text. @match can be
33 * used to dynamically select whether to render a given media item as
34 * an image or text (on the server) and then fetch the corresponding
35 * React component and its data dependencies (information about the
36 * image or about the text).
37 *
38 * ```
39 * // Media.react.js
40 *
41 * // Define a React component that uses <MatchContainer> to render the
42 * // results of a @module selection
43 * function Media(props) {
44 * const {media, ...restPropsj} = props;
45 *
46 * const loader = moduleReference => {
47 * // given the data returned by your server for the @module directive,
48 * // return the React component (or throw a Suspense promise if
49 * // it is loading asynchronously).
50 * todo_returnModuleOrThrowPromise(moduleReference);
51 * };
52 * return <MatchContainer match={media.mediaAttachment} props={restProps} />;
53 * }
54 *
55 * module.exports = createSuspenseFragmentContainer(
56 * Media,
57 * {
58 * media: graphql`
59 * fragment Media_media on Media {
60 * # ...
61 * mediaAttachment @match {
62 * ...ImageContainer_image @module(name: "ImageContainer.react")
63 * ...TextContainer_text @module(name: "TextContainer.react")
64 * }
65 * }
66 * `
67 * },
68 * );
69 * ```
70 *
71 * ## API
72 *
73 * MatchContainer accepts the following props:
74 * - `match`: The results (an opaque object) of a `@match` field.
75 * - `props`: Props that should be passed through to the dynamically
76 * selected component. Note that any of the components listed in
77 * `@module()` could be selected, so all components should accept
78 * the value passed here.
79 * - `loader`: A function to load a module given a reference (whatever
80 * your server returns for the `js(moduleName: String)` field).
81 *
82 */
83// Note: this type is intentionally non-exact, it is expected that the
84// object may contain sibling fields.
85
86function MatchContainer(_ref2) {
87 var _ref;
88
89 var fallback = _ref2.fallback,
90 loader = _ref2.loader,
91 match = _ref2.match,
92 props = _ref2.props;
93
94 if (match != null && typeof match !== 'object') {
95 throw new Error('MatchContainer: Expected `match` value to be an object or null/undefined.');
96 } // NOTE: the MatchPointer type has a $fragmentRefs field to ensure that only
97 // an object that contains a FragmentSpread can be passed. If the fragment
98 // spread matches, then the metadata fields below (__id, __fragments, etc)
99 // will be present. But they can be missing if all the fragment spreads use
100 // @module and none of the types matched. The cast here is necessary because
101 // fragment Flow types don't describe metadata fields, only the actual schema
102 // fields the developer selected.
103
104
105 var _ref3 = (_ref = match) !== null && _ref !== void 0 ? _ref : {},
106 __id = _ref3.__id,
107 __fragments = _ref3.__fragments,
108 __fragmentOwner = _ref3.__fragmentOwner,
109 __fragmentPropName = _ref3.__fragmentPropName,
110 __module_component = _ref3.__module_component;
111
112 if (__fragmentOwner != null && typeof __fragmentOwner !== 'object' || __fragmentPropName != null && typeof __fragmentPropName !== 'string' || __fragments != null && typeof __fragments !== 'object' || __id != null && typeof __id !== 'string') {
113 throw new Error("MatchContainer: Invalid 'match' value, expected an object that has a " + "'...SomeFragment' spread.");
114 }
115
116 var LoadedContainer = __module_component != null ? loader(__module_component) : null;
117 var fragmentProps = useMemo(function () {
118 // TODO: Perform this transformation in RelayReader so that unchanged
119 // output of subscriptions already has a stable identity.
120 if (__fragmentPropName != null && __id != null && __fragments != null) {
121 var fragProps = {};
122 fragProps[__fragmentPropName] = {
123 __id: __id,
124 __fragments: __fragments,
125 __fragmentOwner: __fragmentOwner
126 };
127 return fragProps;
128 }
129
130 return null;
131 }, [__id, __fragments, __fragmentOwner, __fragmentPropName]);
132
133 if (LoadedContainer != null && fragmentProps != null) {
134 return React.createElement(LoadedContainer, (0, _extends2["default"])({}, props, fragmentProps));
135 } else {
136 var _fallback;
137
138 return (_fallback = fallback) !== null && _fallback !== void 0 ? _fallback : null;
139 }
140}
141
142module.exports = MatchContainer;
\No newline at end of file