Skip to content

Commit 23d131a

Browse files
flexzuuJames Baxley
authored andcommitted
Add support for client in options (apollographql#729)
* add support for client in options Adds support for client in graphql HoC to support usecases with more than one ApolloClient * Client is no longer required in context because it can get passed via options. * Dont use calculateOptions if using a mutation. Because it checks if variables are present * Use mapProps instead of calcluateOptions for componentWillReceiveProps. * Update client if client props change. * Skip-Test now no longer expects option.client to be not accesed. * Add test for client in options. * Update Changelog.md
1 parent f847d08 commit 23d131a

File tree

4 files changed

+102
-7
lines changed

4 files changed

+102
-7
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Change log
22

33
### vNext
4+
- Feature: You can now supply a client in options object passed to the `graphql` high oder component. [PR #729]
45

56
### 1.4.2
67
- Fix: Fix component reference and variable statement for flow types

src/graphql.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ export declare interface MutationOpts {
3939
variables?: Object;
4040
optimisticResponse?: Object;
4141
updateQueries?: MutationQueryReducersMap;
42+
client?: ApolloClient;
4243
}
4344

4445
export declare interface QueryOpts {
4546
ssr?: boolean;
4647
variables?: { [key: string]: any };
4748
fetchPolicy?: FetchPolicy;
4849
pollInterval?: number;
50+
client?: ApolloClient;
4951
// deprecated
5052
skip?: boolean;
5153
}
@@ -161,7 +163,7 @@ export default function graphql<TResult = {}, TProps = {}, TChildProps = Default
161163
static displayName = graphQLDisplayName;
162164
static WrappedComponent = WrappedComponent;
163165
static contextTypes = {
164-
client: PropTypes.object.isRequired,
166+
client: PropTypes.object,
165167
};
166168

167169
// react / redux and react dev tools (HMR) needs
@@ -191,7 +193,13 @@ export default function graphql<TResult = {}, TProps = {}, TChildProps = Default
191193
constructor(props, context) {
192194
super(props, context);
193195
this.version = version;
194-
this.client = context.client;
196+
197+
const { client } = mapPropsToOptions(props);
198+
if (client) {
199+
this.client = client;
200+
} else {
201+
this.client = context.client;
202+
}
195203

196204
invariant(!!this.client,
197205
`Could not find "client" in the context of ` +
@@ -219,14 +227,19 @@ export default function graphql<TResult = {}, TProps = {}, TChildProps = Default
219227
}
220228

221229
componentWillReceiveProps(nextProps, nextContext) {
222-
if (shallowEqual(this.props, nextProps) && this.client === nextContext.client) {
230+
const { client } = mapPropsToOptions(nextProps);
231+
if (shallowEqual(this.props, nextProps) && (this.client === client || this.client === nextContext.client)) {
223232
return;
224233
}
225234

226235
this.shouldRerender = true;
227236

228-
if (this.client !== nextContext.client) {
229-
this.client = nextContext.client;
237+
if (this.client !== client && this.client !== nextContext.client) {
238+
if (client) {
239+
this.client = client;
240+
} else {
241+
this.client = nextContext.client;
242+
}
230243
this.unsubscribeFromQuery();
231244
this.queryObservable = null;
232245
this.previousData = {};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/// <reference types="jest" />
2+
3+
import * as React from 'react';
4+
import * as PropTypes from 'prop-types';
5+
import * as ReactDOM from 'react-dom';
6+
import * as renderer from 'react-test-renderer';
7+
import { mount, shallow } from 'enzyme';
8+
import gql from 'graphql-tag';
9+
import ApolloClient, { ApolloError, ObservableQuery } from 'apollo-client';
10+
import { mockNetworkInterface } from '../../../../src/test-utils';
11+
import { ApolloProvider, graphql} from '../../../../src';
12+
13+
describe('client option', () => {
14+
15+
it('renders with client from options', () => {
16+
const query = gql`query people { allPeople(first: 1) { people { name } } }`;
17+
const data = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } };
18+
const networkInterface = mockNetworkInterface({ request: { query }, result: { data } });
19+
const client = new ApolloClient({ networkInterface, addTypename: false });
20+
const config = {
21+
options: {
22+
client,
23+
}
24+
};
25+
const ContainerWithData = graphql(query, config)((props) => null);
26+
shallow(<ContainerWithData />);
27+
});
28+
29+
it('ignores client from context if client from options is present', (done) => {
30+
const query = gql`query people { allPeople(first: 1) { people { name } } }`;
31+
const dataProvider = { allPeople: { people: [ { name: 'Leia Organa Solo' } ] } };
32+
const networkInterfaceProvider = mockNetworkInterface({ request: { query }, result: { data: dataProvider } });
33+
const clientProvider = new ApolloClient({ networkInterface: networkInterfaceProvider, addTypename: false });
34+
const dataOptions = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } };
35+
const networkInterfaceOptions = mockNetworkInterface({ request: { query }, result: { data: dataOptions } });
36+
const clientOptions = new ApolloClient({ networkInterface: networkInterfaceOptions, addTypename: false });
37+
38+
const config = {
39+
options: {
40+
client: clientOptions,
41+
}
42+
};
43+
44+
class Container extends React.Component<any, any> {
45+
componentWillReceiveProps({ data }) { // tslint:disable-line
46+
expect(data.loading).toBe(false); // first data
47+
expect(data.allPeople).toMatchObject({ people: [ { name: 'Luke Skywalker' } ] });
48+
done();
49+
}
50+
render() {
51+
return null;
52+
}
53+
};
54+
const ContainerWithData = graphql(query, config)(Container);
55+
renderer.create(<ApolloProvider client={clientProvider}><ContainerWithData /></ApolloProvider>);
56+
57+
});
58+
it('exposes refetch as part of the props api', (done) => {
59+
const query = gql`query people($first: Int) { allPeople(first: $first) { people { name } } }`;
60+
const variables = { first: 1 };
61+
const data1 = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } };
62+
const networkInterface = mockNetworkInterface(
63+
{ request: { query, variables }, result: { data: data1 } },
64+
);
65+
const client = new ApolloClient({ networkInterface, addTypename: false });
66+
67+
let hasRefetched, count = 0;
68+
@graphql(query)
69+
class Container extends React.Component<any, any> {
70+
componentWillReceiveProps({ data }) { // tslint:disable-line
71+
expect(data.loading).toBe(false); // first data
72+
done();
73+
}
74+
render() {
75+
return null;
76+
}
77+
};
78+
79+
renderer.create(<ApolloProvider client={client}><Container first={1} /></ApolloProvider>);
80+
});
81+
});

test/react-web/client/graphql/queries/skip.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ describe('[queries] skip', () => {
123123
renderer.create(<ApolloProvider client={client}><Parent /></ApolloProvider>);
124124
});
125125

126-
it('doesn\'t run options or props when skipped', (done) => {
126+
it('doesn\'t run options or props when skipped, except option.client', (done) => {
127127
const query = gql`query people { allPeople(first: 1) { people { name } } }`;
128128
const data = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } };
129129
const networkInterface = mockNetworkInterface({ request: { query }, result: { data } });
@@ -132,7 +132,7 @@ describe('[queries] skip', () => {
132132
let queryExecuted;
133133
@graphql(query, {
134134
skip: ({ skip }) => skip,
135-
options: ({ willThrowIfAccesed }) => ({ pollInterval: willThrowIfAccesed.pollInterval }),
135+
options: ({ client, ...willThrowIfAccesed }) => ({ pollInterval: willThrowIfAccesed.pollInterval }),
136136
props: ({ willThrowIfAccesed }) => ({ pollInterval: willThrowIfAccesed.pollInterval }),
137137
})
138138
class Container extends React.Component<any, any> {

0 commit comments

Comments
 (0)