diff --git a/Changelog.md b/Changelog.md index d71e273b86..b1a705cb52 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,7 +3,7 @@ Expect active development and potentially significant breaking changes in the `0.x` track. We'll try to be diligent about releasing a `1.0` version in a timely fashion (ideally within 1 or 2 months), so that we can take advantage of SemVer to signify breaking changes from that point on. ### vNext -- ApolloProvider now changes it's client and store when those props change. [PR #479](https://github.com/apollographql/react-apollo/pull/479) +- ApolloProvider now changes its client and store when those props change. [PR #479](https://github.com/apollographql/react-apollo/pull/479) ### 0.11.2 - Remove `@types/chai` dev dependency which called a reference to the `chai` types in the production build. [PR #471](https://github.com/apollographql/react-apollo/pull/471) diff --git a/src/ApolloProvider.tsx b/src/ApolloProvider.tsx index 771691c95c..455f9a6167 100644 --- a/src/ApolloProvider.tsx +++ b/src/ApolloProvider.tsx @@ -10,7 +10,9 @@ import { Store, } from 'redux'; -import ApolloClient from 'apollo-client'; +/* tslint:disable:no-unused-variable */ +import ApolloClient, { ApolloStore } from 'apollo-client'; +/* tslint:enable:no-unused-variable */ import invariant = require('invariant'); @@ -37,44 +39,32 @@ export default class ApolloProvider extends Component { client: PropTypes.object.isRequired, }; - public store: Store; - public client: ApolloClient; - constructor(props, context) { super(props, context); - this._init(props); - } - - componentWillReceiveProps(nextProps) { - this._init(nextProps); - } - - _init(props) { invariant( props.client, 'ApolloClient was not passed a client instance. Make ' + 'sure you pass in your client via the "client" prop.' ); - this.client = props.client; + if (!props.store) { props.client.initStore(); } + } - if (props.store) { - this.store = props.store; - // support an immutable store alongside apollo store - if (props.immutable) props.client.initStore(); - return; - } + shouldComponentUpdate(nextProps) { + return this.props.client !== nextProps.client || + this.props.store !== nextProps.store || + this.props.children !== nextProps.children; + } - // intialize the built in store if none is passed in - props.client.initStore(); - this.store = props.client.store; + componentDidUpdate() { + if (!this.props.store) { this.props.client.initStore(); } } getChildContext() { return { - store: this.store, - client: this.client, + store: this.props.store || this.props.client.store, + client: this.props.client, }; } diff --git a/test/react-web/client/ApolloProvider.test.tsx b/test/react-web/client/ApolloProvider.test.tsx index 81b2b8acbd..763d7bb158 100644 --- a/test/react-web/client/ApolloProvider.test.tsx +++ b/test/react-web/client/ApolloProvider.test.tsx @@ -29,10 +29,39 @@ describe(' Component', () => { return
; } } + class Container extends React.Component { + constructor(props) { + super(props) + this.state = {} + } + + componentDidMount() { + this.setState({ + store: this.props.store, + client: this.props.client + }) + } + + render() { + if (this.state.store || this.props.store) { + return ( + + + + ) + } else { + return ( + + + + ) + } + } + }; const client = new ApolloClient(); const store = createStore(() => ({})); - it('should render children components', () => { const wrapper = shallow( @@ -103,4 +132,68 @@ describe(' Component', () => { expect(child.context.store).toEqual(store); }); + + it('should update props when the client changes', () => { + const container = shallow(); + expect(container.find(ApolloProvider).props().client).toEqual(client); + + const newClient = new ApolloClient(); + container.setState({ client: newClient }); + expect(container.find(ApolloProvider).props().client).toEqual(newClient); + expect(container.find(ApolloProvider).props().client).not.toEqual(client); + }); + + it('should update props when the store changes', () => { + const container = shallow(); + expect(container.find(ApolloProvider).props().store).toEqual(store); + + const newStore = createStore(() => ({})); + container.setState({ store: newStore }); + expect(container.find(ApolloProvider).props().store).toEqual(newStore); + expect(container.find(ApolloProvider).props().store).not.toEqual(store); + }); + + it('should call clients init store when a store is not passed', () => { + const testClient = new ApolloClient(); + testClient.store = store; + + const initStoreMock = jest.fn(); + testClient.initStore = initStoreMock; + + const container = TestUtils.renderIntoDocument( + + ) as React.Component; + expect(initStoreMock).toHaveBeenCalled(); + + initStoreMock.mockClear(); + const newClient = new ApolloClient(); + newClient.store = store; + newClient.initStore = initStoreMock; + container.setState({ client: newClient }); + + expect(initStoreMock).toHaveBeenCalled(); + }) + + it('should not call clients init store when a store is passed', () => { + const testClient = new ApolloClient(); + + const initStoreMock = jest.fn(); + testClient.initStore = initStoreMock; + + const container = TestUtils.renderIntoDocument( + + ) as React.Component; + + expect(initStoreMock).not.toHaveBeenCalled(); + + initStoreMock.mockClear(); + const newClient = new ApolloClient(); + container.setState({ client: newClient }); + + expect(initStoreMock).not.toHaveBeenCalled(); + + const newStore = createStore(() => ({})); + container.setState({ store: store }); + expect(initStoreMock).not.toHaveBeenCalled(); + }) });