/
buildReactRelayContainer.js
106 lines (92 loc) · 3.38 KB
/
buildReactRelayContainer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const React = require('React');
const ReactRelayContext = require('./ReactRelayContext');
const assertFragmentMap = require('./assertFragmentMap');
const invariant = require('invariant');
const mapObject = require('mapObject');
const readContext = require('./readContext');
const {
getComponentName,
getContainerName,
} = require('./ReactRelayContainerUtils');
import type {GeneratedNodeMap} from './ReactRelayTypes';
import type {FragmentMap} from 'relay-runtime';
type ContainerCreator = (
Component: React$ComponentType<any>,
fragments: FragmentMap,
) => React$ComponentType<any>;
/**
* Creates a component class whose instances adapt to the
* `context.relay.environment` in which they are rendered and which have the
* necessary static methods (`getFragment()` etc) to be composed within classic
* `Relay.Containers`.
*/
function buildReactRelayContainer<TBase: React$ComponentType<*>>(
ComponentClass: TBase,
fragmentSpec: GeneratedNodeMap,
createContainerWithFragments: ContainerCreator,
): TBase {
// Sanity-check user-defined fragment input
const containerName = getContainerName(ComponentClass);
assertFragmentMap(getComponentName(ComponentClass), fragmentSpec);
// Memoize a container for the last environment instance encountered
let environment;
let Container;
function ContainerConstructor(props) {
if (Container == null || props.__relayContext.environment !== environment) {
environment = props.__relayContext.environment;
if (__DEV__) {
const {isRelayModernEnvironment} = require('relay-runtime');
if (!isRelayModernEnvironment(environment)) {
throw new Error(
'RelayModernContainer: Can only use Relay Modern component ' +
`${containerName} in a Relay Modern environment!`,
);
}
}
const {getFragment: getFragmentFromTag} = environment.unstable_internal;
const fragments = mapObject(fragmentSpec, getFragmentFromTag);
Container = createContainerWithFragments(ComponentClass, fragments);
// Attach static lifecycle to wrapper component so React can see it.
ContainerConstructor.getDerivedStateFromProps = (Container: any).getDerivedStateFromProps;
}
// $FlowFixMe
return new Container(props);
}
ContainerConstructor.prototype = React.Component.prototype;
function forwardRef(props, ref) {
const context = readContext(ReactRelayContext);
invariant(
context,
`${containerName} tried to render a context that was ` +
`not valid this means that ${containerName} was rendered outside of a ` +
'query renderer.',
);
return (
<ContainerConstructor
{...props}
__relayContext={context}
componentRef={props.componentRef || ref}
/>
);
}
forwardRef.displayName = containerName;
const ForwardContainer = React.forwardRef(forwardRef);
if (__DEV__) {
// Used by RelayModernTestUtils
(ForwardContainer: any).__ComponentClass = ComponentClass;
}
/* $FlowFixMe(>=0.89.0 site=www,mobile,react_native_fb,oss) Suppressing errors
* found while preparing to upgrade to 0.89.0 */
return ForwardContainer;
}
module.exports = buildReactRelayContainer;