This repository has been archived by the owner on Apr 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 787
/
subscription-hoc.tsx
145 lines (134 loc) · 4.55 KB
/
subscription-hoc.tsx
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import React from 'react';
import { DocumentNode } from 'graphql';
import hoistNonReactStatics from 'hoist-non-react-statics';
import { parser, BaseQueryOptions } from '@apollo/react-common';
import { Subscription } from '@apollo/react-components';
import {
getDisplayName,
GraphQLBase,
calculateVariablesFromProps,
defaultMapPropsToOptions,
defaultMapPropsToSkip
} from './hoc-utils';
import { OperationOption, OptionProps, DataProps } from './types';
export function withSubscription<
TProps extends TGraphQLVariables | {} = {},
TData = {},
TGraphQLVariables = {},
TChildProps = DataProps<TData, TGraphQLVariables>
>(
document: DocumentNode,
operationOptions: 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) => BaseQueryOptions;
if (typeof mapPropsToOptions !== 'function')
mapPropsToOptions = () => options as BaseQueryOptions;
let mapPropsToSkip = skip as (props: any) => boolean;
if (typeof mapPropsToSkip !== 'function') mapPropsToSkip = () => skip as any;
// allow for advanced referential equality checks
let lastResultProps: TChildProps | void;
return (
WrappedComponent: React.ComponentType<TProps & TChildProps>
): 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 };
}
componentDidUpate(prevProps: TProps) {
if (shouldResubscribe) {
this.setState({
resubscribe: shouldResubscribe(prevProps, this.props)
});
}
}
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);
}
return (
<Subscription
{...opts}
displayName={graphQLDisplayName}
skip={shouldSkip}
subscription={document}
shouldResubscribe={this.state.resubscribe}
>
{({ data, ...r }: any) => {
if (operationOptions.withRef) {
this.withRef = true;
props = Object.assign({}, props, {
ref: this.setWrappedInstance
});
}
// if we have skipped, no reason to manage any reshaping
if (shouldSkip) {
return (
<WrappedComponent
{...props as TProps}
{...{} as TChildProps}
/>
);
}
// 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 };
if (operationOptions.props) {
const newResult: OptionProps<
TProps,
TData,
TGraphQLVariables
> = {
[name]: result,
ownProps: props as TProps
};
lastResultProps = operationOptions.props(
newResult,
lastResultProps
);
childProps = lastResultProps;
}
return (
<WrappedComponent
{...props as TProps}
{...childProps as TChildProps}
/>
);
}}
</Subscription>
);
}
}
// Make sure we preserve any custom statics on the original component.
return hoistNonReactStatics(GraphQL, WrappedComponent, {});
};
}