diff --git a/Changelog.md b/Changelog.md
index 7188b40123..e5c0d8b9cd 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -10,6 +10,9 @@
- Adjust the `ApolloContext` definition to play a bit more nicely with
`React.createContext` types.
[@JoviDeCroock](https://github.com/JoviDeCroock) in [#3018](https://github.com/apollographql/react-apollo/pull/3018)
+- The result of a mutation is now made available to the wrapped component,
+ when using the `graphql` HOC.
+ [@andycarrell](https://github.com/andycarrell) in [#3008](https://github.com/apollographql/react-apollo/pull/3008)
### Bug Fixes
diff --git a/src/mutation-hoc.tsx b/src/mutation-hoc.tsx
index 6726b5cfa7..aaf73d0c7b 100644
--- a/src/mutation-hoc.tsx
+++ b/src/mutation-hoc.tsx
@@ -56,12 +56,19 @@ export function withMutation<
return (
- {(mutate, _result) => {
+ {(mutate, { data, ...r }) => {
+ // the HOC's historically hoisted the data from the execution result
+ // up onto the result since it was passed as a nested prop
+ // we massage the Mutation component's shape here to replicate that
+ // this matches the query HoC
+ const result = Object.assign(r, data || {});
const name = operationOptions.name || 'mutate';
- let childProps = { [name]: mutate };
+ const resultName = operationOptions.name ? `${name}Result` : 'result';
+ let childProps = { [name]: mutate, [resultName]: result };
if (operationOptions.props) {
const newResult: OptionProps = {
[name]: mutate,
+ [resultName]: result,
ownProps: props,
};
childProps = operationOptions.props(newResult) as any;
@@ -70,7 +77,7 @@ export function withMutation<
return (
);
}}
diff --git a/src/types.ts b/src/types.ts
index a8b1d3e1b3..2d20e989a1 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,146 +1,147 @@
-import ApolloClient, {
- ApolloQueryResult,
- ApolloError,
- FetchPolicy,
- WatchQueryFetchPolicy,
- ErrorPolicy,
- FetchMoreOptions,
- UpdateQueryOptions,
- FetchMoreQueryOptions,
- SubscribeToMoreOptions,
- PureQueryOptions,
- MutationUpdaterFn,
-} from 'apollo-client';
-import { MutationFn } from './Mutation';
-
-export interface Context {
- [key: string]: any;
-};
-
-export type OperationVariables = {
- [key: string]: any;
-};
-
-/**
- * Function which returns an array of query names or query objects for refetchQueries option.
- * Allows conditional refetches.
- */
-export type RefetchQueriesProviderFn = (...args: any[]) => Array;
-
-export interface MutationOpts {
- variables?: TGraphQLVariables;
- optimisticResponse?: TData;
- refetchQueries?: Array | RefetchQueriesProviderFn;
- awaitRefetchQueries?: boolean;
- errorPolicy?: ErrorPolicy;
- update?: MutationUpdaterFn;
- client?: ApolloClient;
- notifyOnNetworkStatusChange?: boolean;
- context?: Context;
- onCompleted?: (data: TData) => void;
- onError?: (error: ApolloError) => void;
- fetchPolicy?: FetchPolicy;
-}
-
-export interface QueryOpts {
- ssr?: boolean;
- variables?: TGraphQLVariables;
- fetchPolicy?: WatchQueryFetchPolicy;
- errorPolicy?: ErrorPolicy;
- pollInterval?: number;
- client?: ApolloClient;
- notifyOnNetworkStatusChange?: boolean;
- context?: Context;
- partialRefetch?: boolean;
- returnPartialData?: boolean;
-}
-
-export interface QueryControls {
- error?: ApolloError;
- networkStatus: number;
- loading: boolean;
- variables: TGraphQLVariables;
- fetchMore: (
- fetchMoreOptions: FetchMoreQueryOptions &
- FetchMoreOptions,
- ) => Promise>;
- refetch: (variables?: TGraphQLVariables) => Promise>;
- startPolling: (pollInterval: number) => void;
- stopPolling: () => void;
- subscribeToMore: (options: SubscribeToMoreOptions) => () => void;
- updateQuery: (mapFn: (previousQueryResult: any, options: UpdateQueryOptions) => any) => void;
-}
-
-// XXX remove in the next breaking semver change (3.0)
-export interface GraphqlQueryControls
- extends QueryControls {}
-
-// XXX remove in the next breaking semver change (3.0)
-export type MutationFunc = MutationFn<
- TData,
- TVariables
->;
-
-export type DataValue = QueryControls<
- TData,
- TGraphQLVariables
-> &
- // data may not yet be loaded
- Partial;
-
-// export to allow usage individually for simple components
-export interface DataProps {
- data: DataValue;
-}
-
-// export to allow usage individually for simple components
-export interface MutateProps {
- mutate: MutationFn;
-}
-
-export type ChildProps = TProps &
- Partial> &
- Partial>;
-
-export type ChildDataProps<
- TProps = {},
- TData = {},
- TGraphQLVariables = OperationVariables
-> = TProps & DataProps;
-
-export type ChildMutateProps<
- TProps = {},
- TData = {},
- TGraphQLVariables = OperationVariables
-> = TProps & MutateProps;
-
-export type NamedProps = TProps & {
- ownProps: R;
-};
-
-export interface OptionProps
- extends Partial>,
- Partial> {
- ownProps: TProps;
-}
-
-export interface OperationOption<
- TProps,
- TData,
- TGraphQLVariables = OperationVariables,
- TChildProps = ChildProps
-> {
- options?:
- | QueryOpts
- | MutationOpts
- | ((props: TProps) => QueryOpts | MutationOpts);
- props?: (
- props: OptionProps,
- lastProps?: TChildProps | void,
- ) => TChildProps;
- skip?: boolean | ((props: TProps) => boolean);
- name?: string;
- withRef?: boolean;
- shouldResubscribe?: (props: TProps, nextProps: TProps) => boolean;
- alias?: string;
-}
+import ApolloClient, {
+ ApolloQueryResult,
+ ApolloError,
+ FetchPolicy,
+ WatchQueryFetchPolicy,
+ ErrorPolicy,
+ FetchMoreOptions,
+ UpdateQueryOptions,
+ FetchMoreQueryOptions,
+ SubscribeToMoreOptions,
+ PureQueryOptions,
+ MutationUpdaterFn,
+} from 'apollo-client';
+import { MutationFn, MutationResult } from './Mutation';
+
+export interface Context {
+ [key: string]: any;
+};
+
+export type OperationVariables = {
+ [key: string]: any;
+};
+
+/**
+ * Function which returns an array of query names or query objects for refetchQueries option.
+ * Allows conditional refetches.
+ */
+export type RefetchQueriesProviderFn = (...args: any[]) => Array;
+
+export interface MutationOpts {
+ variables?: TGraphQLVariables;
+ optimisticResponse?: TData;
+ refetchQueries?: Array | RefetchQueriesProviderFn;
+ awaitRefetchQueries?: boolean;
+ errorPolicy?: ErrorPolicy;
+ update?: MutationUpdaterFn;
+ client?: ApolloClient;
+ notifyOnNetworkStatusChange?: boolean;
+ context?: Context;
+ onCompleted?: (data: TData) => void;
+ onError?: (error: ApolloError) => void;
+ fetchPolicy?: FetchPolicy;
+}
+
+export interface QueryOpts {
+ ssr?: boolean;
+ variables?: TGraphQLVariables;
+ fetchPolicy?: WatchQueryFetchPolicy;
+ errorPolicy?: ErrorPolicy;
+ pollInterval?: number;
+ client?: ApolloClient;
+ notifyOnNetworkStatusChange?: boolean;
+ context?: Context;
+ partialRefetch?: boolean;
+ returnPartialData?: boolean;
+}
+
+export interface QueryControls {
+ error?: ApolloError;
+ networkStatus: number;
+ loading: boolean;
+ variables: TGraphQLVariables;
+ fetchMore: (
+ fetchMoreOptions: FetchMoreQueryOptions &
+ FetchMoreOptions,
+ ) => Promise>;
+ refetch: (variables?: TGraphQLVariables) => Promise>;
+ startPolling: (pollInterval: number) => void;
+ stopPolling: () => void;
+ subscribeToMore: (options: SubscribeToMoreOptions) => () => void;
+ updateQuery: (mapFn: (previousQueryResult: any, options: UpdateQueryOptions) => any) => void;
+}
+
+// XXX remove in the next breaking semver change (3.0)
+export interface GraphqlQueryControls
+ extends QueryControls {}
+
+// XXX remove in the next breaking semver change (3.0)
+export type MutationFunc = MutationFn<
+ TData,
+ TVariables
+>;
+
+export type DataValue = QueryControls<
+ TData,
+ TGraphQLVariables
+> &
+ // data may not yet be loaded
+ Partial;
+
+// export to allow usage individually for simple components
+export interface DataProps {
+ data: DataValue;
+}
+
+// export to allow usage individually for simple components
+export interface MutateProps {
+ mutate: MutationFn;
+ result: MutationResult;
+}
+
+export type ChildProps = TProps &
+ Partial> &
+ Partial>;
+
+export type ChildDataProps<
+ TProps = {},
+ TData = {},
+ TGraphQLVariables = OperationVariables
+> = TProps & DataProps;
+
+export type ChildMutateProps<
+ TProps = {},
+ TData = {},
+ TGraphQLVariables = OperationVariables
+> = TProps & MutateProps;
+
+export type NamedProps = TProps & {
+ ownProps: R;
+};
+
+export interface OptionProps
+ extends Partial>,
+ Partial> {
+ ownProps: TProps;
+}
+
+export interface OperationOption<
+ TProps,
+ TData,
+ TGraphQLVariables = OperationVariables,
+ TChildProps = ChildProps
+> {
+ options?:
+ | QueryOpts
+ | MutationOpts
+ | ((props: TProps) => QueryOpts | MutationOpts);
+ props?: (
+ props: OptionProps,
+ lastProps?: TChildProps | void,
+ ) => TChildProps;
+ skip?: boolean | ((props: TProps) => boolean);
+ name?: string;
+ withRef?: boolean;
+ shouldResubscribe?: (props: TProps, nextProps: TProps) => boolean;
+ alias?: string;
+}
diff --git a/test/client/graphql/mutations/index.test.tsx b/test/client/graphql/mutations/index.test.tsx
index b29a4c2c6e..be1c34fa06 100644
--- a/test/client/graphql/mutations/index.test.tsx
+++ b/test/client/graphql/mutations/index.test.tsx
@@ -45,9 +45,55 @@ describe('graphql(mutation)', () => {
});
it('binds a mutation to props', () => {
- const ContainerWithData = graphql(query)(({ mutate }) => {
+ const ContainerWithData = graphql(query)(({ mutate, result }) => {
expect(mutate).toBeTruthy();
+ expect(result).toBeTruthy();
expect(typeof mutate).toBe('function');
+ expect(typeof result).toBe('object');
+ return null;
+ });
+
+ renderer.create(
+
+
+ ,
+ );
+ });
+
+ it('binds a mutation result to props', () => {
+ type InjectedProps = {
+ result: any;
+ };
+
+ const ContainerWithData = graphql<{}, Data, Variables, InjectedProps>(query)(({ result }) => {
+ const { loading, error } = result;
+ expect(result).toBeTruthy();
+ expect(typeof loading).toBe('boolean');
+ expect(error).toBeFalsy();
+
+ return null;
+ });
+
+ renderer.create(
+
+
+ ,
+ );
+ });
+
+ it('binds a mutation to props with a custom name', () => {
+ interface Props {};
+
+ type InjectedProps = {
+ customMutation: any;
+ customMutationResult: any;
+ };
+
+ const ContainerWithData = graphql(query, { name: 'customMutation' })(({ customMutation, customMutationResult }) => {
+ expect(customMutation).toBeTruthy();
+ expect(customMutationResult).toBeTruthy();
+ expect(typeof customMutation).toBe('function');
+ expect(typeof customMutationResult).toBe('object');
return null;
});