Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Client scoped query recyclers (#513) #876

Merged
merged 2 commits into from Aug 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
@@ -1,6 +1,7 @@
# Change log

### vNext
- Fix: Scope query recyclers by client [#876](https://github.com/apollographql/react-apollo/pull/876)
- MockNetworkInterface match mock requests regardless of variable order [#973](https://github.com/apollographql/react-apollo/pull/973)

### 1.4.11
Expand Down
7 changes: 6 additions & 1 deletion src/ApolloProvider.tsx
Expand Up @@ -5,6 +5,7 @@ import { Component } from 'react';
import { Store } from 'redux';

import ApolloClient, { ApolloStore } from 'apollo-client';
import QueryRecyclerProvider from './QueryRecyclerProvider';

const invariant = require('invariant');

Expand Down Expand Up @@ -61,6 +62,10 @@ export default class ApolloProvider extends Component<ProviderProps, any> {
}

render() {
return React.Children.only(this.props.children);
return (
<QueryRecyclerProvider>
{React.Children.only(this.props.children)}
</QueryRecyclerProvider>
);
}
}
50 changes: 50 additions & 0 deletions src/QueryRecyclerProvider.tsx
@@ -0,0 +1,50 @@
import { Component, Children } from 'react';
import * as PropTypes from 'prop-types';
import { ObservableQueryRecycler } from './queryRecycler';

class QueryRecyclerProvider extends Component {
static propTypes = {
children: PropTypes.element.isRequired
};

static contextTypes = {
client: PropTypes.object
};

static childContextTypes = {
getQueryRecycler: PropTypes.func.isRequired
};

private recyclers: WeakMap<Component, ObservableQueryRecycler>;

constructor(props) {
super(props);
this.recyclers = new WeakMap();
this.getQueryRecycler = this.getQueryRecycler.bind(this);
}

componentWillReceiveProps(nextProps, nextContext) {
if (this.context.client !== nextContext.client) {
this.recyclers = new WeakMap();
}
}

getQueryRecycler(component) {
if (!this.recyclers.has(component)) {
this.recyclers.set(component, new ObservableQueryRecycler());
}
return this.recyclers.get(component);
}

getChildContext() {
return ({
getQueryRecycler: this.getQueryRecycler
});
}

render() {
return Children.only(this.props.children);
}
}

export default QueryRecyclerProvider;
20 changes: 10 additions & 10 deletions src/graphql.tsx
Expand Up @@ -98,19 +98,12 @@ export default function graphql<
function wrapWithApolloComponent(WrappedComponent) {
const graphQLDisplayName = `${alias}(${getDisplayName(WrappedComponent)})`;

// A recycler that we can use to recycle old observable queries to keep
// them hot between component unmounts and remounts.
//
// Note that the existence of this recycler could potentially cause memory
// leaks if many components are being created and unmounted in parallel.
// However, this is an unlikely scenario.
const recycler = new ObservableQueryRecycler();

class GraphQL extends Component<TProps, void> {
static displayName = graphQLDisplayName;
static WrappedComponent = WrappedComponent;
static contextTypes = {
client: PropTypes.object,
getQueryRecycler: PropTypes.func,
};

// react / redux and react dev tools (HMR) needs
Expand All @@ -121,6 +114,7 @@ export default function graphql<
// data storage
private store: ApolloStore;
private client: ApolloClient; // apollo client
private recycler: ObservableQueryRecycler;
private type: DocumentType;

// request / action storage. Note that we delete querySubscription if we
Expand All @@ -142,6 +136,7 @@ export default function graphql<

constructor(props, context) {
super(props, context);

this.version = version;
this.type = operation.type;
this.dataForChildViaMutation = this.dataForChildViaMutation.bind(this);
Expand Down Expand Up @@ -183,6 +178,7 @@ export default function graphql<
this.unsubscribeFromQuery();
this.queryObservable = null;
this.previousData = {};

this.updateQuery(nextProps);
if (!this.shouldSkip(nextProps)) {
this.subscribeToQuery();
Expand Down Expand Up @@ -219,7 +215,7 @@ export default function graphql<
if (this.type === DocumentType.Query) {
// Recycle the query observable if there ever was one.
if (this.queryObservable) {
recycler.recycle(this.queryObservable);
this.getQueryRecycler().recycle(this.queryObservable);
delete this.queryObservable;
}

Expand All @@ -237,6 +233,10 @@ export default function graphql<
this.hasMounted = false;
}

getQueryRecycler() {
return this.context.getQueryRecycler(GraphQL);
}

getClient(): ApolloClient {
if (this.client) return this.client;
const { client } = mapPropsToOptions(this.props);
Expand Down Expand Up @@ -337,7 +337,7 @@ export default function graphql<
// Try to reuse an `ObservableQuery` instance from our recycler. If
// we get null then there is no instance to reuse and we should
// create a new `ObservableQuery`. Otherwise we will use our old one.
const queryObservable = recycler.reuse(opts);
const queryObservable = this.getQueryRecycler().reuse(opts);

if (queryObservable === null) {
this.queryObservable = this.getClient().watchQuery(
Expand Down
3 changes: 2 additions & 1 deletion test/react-web/client/graphql/client-option.test.tsx
Expand Up @@ -9,6 +9,7 @@ import gql from 'graphql-tag';
import ApolloClient, { ApolloError, ObservableQuery } from 'apollo-client';
import { mockNetworkInterface } from '../../../../src/test-utils';
import { ApolloProvider, graphql } from '../../../../src';
import { ObservableQueryRecycler } from '../../../../src/queryRecycler';

describe('client option', () => {
it('renders with client from options', () => {
Expand All @@ -33,7 +34,7 @@ describe('client option', () => {
},
};
const ContainerWithData = graphql(query, config)(props => null);
shallow(<ContainerWithData />);
shallow(<ContainerWithData />, { context: { getQueryRecycler: () => new ObservableQueryRecycler() }});
});

it('ignores client from context if client from options is present', done => {
Expand Down