react-apollo-hooks
react-apollo-hooks copied to clipboard
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.