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 = () => ();
+
+ 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;
}