onCompleted and onError not working with mutation
Hey, great library.
I'm trying to catch mutation errors but it seems none of the callbacks are called. The only way I was able to catch errors is to use errorPolicy: "ignore" and then checking the result in refetchQueries callback.
Is it an error or am I doing something wrong?
addUser = useMutation(mutation, {
update: (proxy, mutationResult) => {
/* NOT CALLED */
},
refetchQueries: (result) => {
/* CALLED ON errorPolicy: "ignore" */
},
onCompleted: (data) => {
/* NOT CALLED */
},
onError: (data) => {
/* NOT CALLED */
}
});
Hi @shlomokraus
Right now you can use only the mutation options supported directly by apollo-client. onCompleted and onError are react-apollo specific. You can have a similar result with something like that:
const addUser = useMutation(mutation);
const myHandler = () => {
addUser().then(
result => {
// success callback
},
error => {
// error callback
}
);
}
Or with async/await syntax:
const addUser = useMutation(mutation);
async function myHandler() {
let result;
try {
result = await addUser();
} catch (error) {
// error handler
}
}
And then:
<button onClick={myHandler}>Add user</button>
Got it. So why not use the react-apollo client instead? After all the library name is react-apollo-hooks :)
Actually, I see that the onCompleted comes from the Component of react-apollo and not from the client itself so it is not relevant
I've opened https://github.com/apollographql/apollo-client/pull/4239
I needed this and loading so I made a wrapper hook. To those who may find it useful:
import { useState } from 'react';
import { useMutation as useHookMutation } from 'react-apollo-hooks';
export function useMutation(
mutation,
{ onCompleted, onError, ...options } = {}
) {
const [loading, setLoading] = useState(false);
const [called, setCalled] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const mutate = useHookMutation(mutation, options);
const handler = async (...args) => {
setLoading(true);
setCalled(true);
setError(null);
setData(null);
try {
const { data } = await mutate(...args);
setData(data);
setLoading(false);
if (onCompleted) {
onCompleted(data);
}
return { data };
} catch (e) {
setLoading(false);
setError(e);
if (onError) {
onError(e);
}
}
};
return [
handler,
{
loading,
called,
error,
data
}
];
}
@zebapy I used your technic but it causes re-render my component many time.
@zebapy it's not super optimized as I'm just getting the hang of hooks, so I'm open to suggestions :) or just wait until these libs have their own. I would imagine it would rerender on each state change regardless?
yes it's rerender on each state change.
@zakiullahbarakzai are you using useEffect hook? I've had rerendering issue as well and solved it with useEffect.
Just in case somebody lands here I have a typescript-enhanced hook based on @zebapy's comment:
import { useState } from "react";
import { OperationVariables } from "apollo-client";
import {
useMutation as useHookMutation,
MutationFn,
MutationHookOptions
} from "react-apollo-hooks";
import { DocumentNode } from "graphql";
interface CustomMutationHookOptions<TData, TVariables, TCache = object>
extends MutationHookOptions<TData, TVariables, TCache> {
onCompleted?(data: TData): void;
onError?(e: Error): void;
}
interface MutationState<TData> {
loading: boolean;
called: boolean;
error?: any;
data?: TData;
}
export function useMutation<
TData,
TVariables = OperationVariables,
TCache = object
>(
mutation: DocumentNode,
options?: CustomMutationHookOptions<TData, TVariables, TCache>
): [MutationFn<TData, TVariables>, MutationState<TData>] {
if (options === undefined) {
options = {};
}
const { onCompleted, onError, ...restOperationVars } = options;
const [state, setState] = useState<MutationState<TData>>({
loading: false,
called: false
});
const mutate = useHookMutation<TData, TVariables, TCache>(
mutation,
restOperationVars
);
const handler: MutationFn<TData, TVariables> = async (...args) => {
setState({
loading: true,
called: true
});
try {
const { data } = await mutate(...args);
setState({ ...state, loading: false, data: data });
if (onCompleted && data !== undefined) {
onCompleted(data);
}
return { data };
} catch (e) {
setState({ ...state, loading: false, error: e });
if (onError) {
onError(e);
}
return {};
}
};
return [handler, state];
}
Hi @shlomokraus
Right now you can use only the mutation options supported directly by apollo-client.
onCompletedandonErrorare react-apollo specific. You can have a similar result with something like that:const addUser = useMutation(mutation); const myHandler = () => { addUser().then( result => { // success callback }, error => { // error callback } ); }Or with async/await syntax:
const addUser = useMutation(mutation); async function myHandler() { let result; try { result = await addUser(); } catch (error) { // error handler } }And then:
<button onClick={myHandler}>Add user</button>
Usemutation, now returns a tuple, so this will give error as addUser is not a function.
Use this instead :
const [mutateMe, { error, loading, data }] = useMutation(MUTANT)
if (error) {
console.log(`error in mutation ${error}`)
}
See : https://github.com/trojanowski/react-apollo-hooks/pull/93
Right now you can use only the mutation options supported directly by apollo-client.
onCompletedandonErrorare react-apollo specific.
I'm confused, from the documentation: https://www.apollographql.com/docs/react/data/mutations/#usemutation-api
The useMutation hook accepts the following options: onCompleted | (data: TData) => void | A callback executed once your mutation successfully completes
@plus- that is official react apollo hook documentation, which is not this repo.