diff --git a/Changelog.md b/Changelog.md index 48b6dc18b3..da064bfce9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,9 @@ Expect active development and potentially significant breaking changes in the `0 ### vNext +- Bug: Fix issue with SSR queries running twice when a mutation wraps a query [#274](https://github.com/apollostack/react-apollo/issue/274) + + ### v0.5.10 - Bug: Fix issue with changing outer props *and not changing variables*, ultimately caused by https://github.com/apollostack/apollo-client/pull/694 diff --git a/src/graphql.tsx b/src/graphql.tsx index 11a6583944..92a042da10 100644 --- a/src/graphql.tsx +++ b/src/graphql.tsx @@ -109,7 +109,7 @@ export function withApollo(WrappedComponent) { } // Make sure we preserve any custom statics on the original component. - return hoistNonReactStatics(WithApollo, WrappedComponent); + return hoistNonReactStatics(WithApollo, WrappedComponent, { fetchData: true }); }; export interface OperationOption { @@ -498,8 +498,7 @@ export default function graphql( if (operation.type === DocumentType.Query) (GraphQL as any).fetchData = fetchData; // Make sure we preserve any custom statics on the original component. - return hoistNonReactStatics(GraphQL, WrappedComponent); - + return hoistNonReactStatics(GraphQL, WrappedComponent, { fetchData: true }); }; }; diff --git a/test/react-web/server/index.test.tsx b/test/react-web/server/index.test.tsx index 825883418d..41eddac900 100644 --- a/test/react-web/server/index.test.tsx +++ b/test/react-web/server/index.test.tsx @@ -54,6 +54,61 @@ describe('SSR', () => { }); }); + it('should pick up queries deep in the render tree', () => { + + const query = gql`{ currentUser { firstName } }`; + const data = { currentUser: { firstName: 'James' } }; + const networkInterface = mockNetworkInterface( + { request: { query }, result: { data }, delay: 50 } + ); + const apolloClient = new ApolloClient({ networkInterface }); + + const WrappedElement = graphql(query)(({ data }) => ( +
{data.loading ? 'loading' : data.currentUser.firstName}
+ )); + + const Page = () => (
Hi
); + + const app = (); + + return getDataFromTree(app) + .then(() => { + const markup = ReactDOM.renderToString(app); + expect(markup).toMatch(/James/); + }); + }); + + it('should handle nested queries that depend on each other', () => { + const idQuery = gql`{ currentUser { id } }`; + const idData = { currentUser: { id: '1234' } }; + const userQuery = gql`query getUser($id: String) { user(id: $id) { firstName } }`; + const variables = { id: '1234' }; + const userData = { user: { firstName: 'James' } }; + const networkInterface = mockNetworkInterface( + { request: { query: idQuery }, result: { data: idData }, delay: 50 }, + { request: { query: userQuery, variables }, result: { data: userData }, delay: 50 }, + ); + const apolloClient = new ApolloClient({ networkInterface }); + + const withId = graphql(idQuery); + const withUser = graphql(userQuery, { + skip: ({ data: { loading } }) => loading, + options: ({ data }) => ({ variables: { id: data.currentUser.id } }), + }); + const Component = ({ data }) => ( +
{data.loading ? 'loading' : data.user.firstName}
+ ); + const WrappedComponent = withId(withUser(Component)); + + const app = (); + + return getDataFromTree(app) + .then(() => { + const markup = ReactDOM.renderToString(app); + expect(markup).toMatch(/James/); + }); + }); + it('should correctly skip queries (deprecated)', () => { const query = gql`{ currentUser { firstName } }`; @@ -264,6 +319,46 @@ describe('SSR', () => { ; }); + it('should correctly handle SSR mutations, reverse order', () => { + + const query = gql`{ currentUser { firstName } }`; + const data1 = { currentUser: { firstName: 'James' } }; + + const mutation = gql`mutation { logRoutes { id } }`; + const mutationData= { logRoutes: { id: 'foo' } }; + + const networkInterface = mockNetworkInterface( + { request: { query }, result: { data: data1 }, delay: 5 }, + { request: { query: mutation }, result: { data: mutationData }, delay: 5 } + ); + const apolloClient = new ApolloClient({ networkInterface }); + + const withQuery = graphql(query, { + props: ({ ownProps, data }) => { + expect(ownProps.mutate).toBeTruthy(); + return { + data, + }; + }, + }); + + const withMutation = graphql(mutation); + const Element = (({ data }) => ( +
{data.loading ? 'loading' : data.currentUser.firstName}
+ )); + + const WrappedElement = withMutation(withQuery(Element)); + + const app = (); + + return getDataFromTree(app) + .then(() => { + const markup = ReactDOM.renderToString(app); + expect(markup).toMatch(/James/); + }) + ; + }); + it('should not require `ApolloProvider` to be the root component', () => { const query = gql`{ currentUser { firstName } }`; diff --git a/typings.d.ts b/typings.d.ts index 101765feed..d3ae7c735d 100644 --- a/typings.d.ts +++ b/typings.d.ts @@ -36,7 +36,7 @@ declare module 'hoist-non-react-statics' { * * Returns the target component. */ - function hoistNonReactStatics(targetComponent: any, sourceComponent: any): any; + function hoistNonReactStatics(targetComponent: any, sourceComponent: any, customStatics: {[name: string]: boolean}): any; namespace hoistNonReactStatics {} export = hoistNonReactStatics; }