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
107
108
109
110
111
112
113
114
115
116
117 | 27x
27x
27x
27x
27x
27x
6x
6x
12x
12x
12x
6x
6x
6x
6x
6x
6x
6x
6x
6x
6x
6x
6x
6x
3x
3x
9x
9x
9x
9x
9x
1x
9x
19x
19x
19x
19x
19x
19x
19x
19x
6x
6x
| import * as React from 'react';
import { DocumentNode } from 'graphql';
const hoistNonReactStatics = require('hoist-non-react-statics');
import { parser } from './parser';
import { OperationOption, QueryOpts, OptionProps, DataProps } from './types';
import { default as Subscription } from './Subscriptions';
import {
getDisplayName,
GraphQLBase,
calculateVariablesFromProps,
defaultMapPropsToOptions,
defaultMapPropsToSkip,
} from './hoc-utils';
export function subscribe<
TProps extends TGraphQLVariables | {} = {},
TData = {},
TGraphQLVariables = {},
TChildProps = Partial<DataProps<TData, TGraphQLVariables>>
>(
document: DocumentNode,
IoperationOptions: OperationOption<TProps, TData, TGraphQLVariables, TChildProps> = {},
) {
// this is memoized so if coming from `graphql` there is nearly no extra cost
const operation = parser(document);
// extract options
const {
options = defaultMapPropsToOptions,
skip = defaultMapPropsToSkip,
alias = 'Apollo',
shouldResubscribe,
} = operationOptions;
let mapPropsToOptions = options as (props: any) => QueryOpts;
Iif (typeof mapPropsToOptions !== 'function') mapPropsToOptions = () => options as QueryOpts;
let mapPropsToSkip = skip as (props: any) => boolean;
Iif (typeof mapPropsToSkip !== 'function') mapPropsToSkip = () => skip as any;
// allow for advanced referential equality checks
let lastResultProps: TChildProps | void;
return (
WrappedComponent: React.ComponentType<TChildProps & TProps>,
): React.ComponentClass<TProps> => {
const graphQLDisplayName = `${alias}(${getDisplayName(WrappedComponent)})`;
class GraphQL extends GraphQLBase<TProps, TChildProps, { resubscribe: boolean }> {
static displayName = graphQLDisplayName;
static WrappedComponent = WrappedComponent;
constructor(props: TProps) {
super(props);
this.state = { resubscribe: false };
}
componentWillReceiveProps(nextProps: TProps) {
Iif (!shouldResubscribe) return;
this.setState({
resubscribe: shouldResubscribe(this.props, nextProps),
});
}
render() {
let props = this.props;
const shouldSkip = mapPropsToSkip(props);
const opts = shouldSkip ? Object.create(null) : mapPropsToOptions(props);
if (!shouldSkip && !opts.variables && operation.variables.length > 0) {
opts.variables = calculateVariablesFromProps(
operation,
props,
graphQLDisplayName,
getDisplayName(WrappedComponent),
);
}
return (
<Subscription
{...opts}
displayName={graphQLDisplayName}
skip={shouldSkip}
subscription={document}
shouldResubscribe={this.state.resubscribe}
>
{({ data, ...r }) => {
Iif (operationOptions.withRef) {
this.withRef = true;
props = Object.assign({}, props, {
ref: this.setWrappedInstance,
});
}
// if we have skipped, no reason to manage any reshaping
Iif (shouldSkip) return <WrappedComponent {...props} />;
// the HOC's historically hoisted the data from the execution result
// up onto the result since it was passed as a nested prop
// we massage the Query components shape here to replicate that
const result = Object.assign(r, data || {});
const name = operationOptions.name || 'data';
let childProps = { [name]: result };
Iif (operationOptions.props) {
const newResult: OptionProps<TProps, TData, TGraphQLVariables> = {
[name]: result,
ownProps: props as TProps,
};
lastResultProps = operationOptions.props(newResult, lastResultProps);
childProps = lastResultProps;
}
return <WrappedComponent {...props} {...childProps} />;
}}
</Subscription>
);
}
}
// Make sure we preserve any custom statics on the original component.
return hoistNonReactStatics(GraphQL, WrappedComponent, {});
};
}
|