Specially useful to run requests against an API, and combined with SWR.
Install it:
$ yarn add use-mutation
Import it:
import useMutation from 'use-mutation';
Create a function which runs a mutation
async function createComment({
authorId,
comment,
}: {
authorId: number;
comment: string;
}) {
const res = await fetch('/api/comments', {
method: 'POST',
body: JSON.stringify({ authorId, comment }),
});
if (!res.ok) throw new Error(res.statusText);
return await res.json();
}
Use your function with useMutation
function CommentForm({ authorId }) {
const [comment, setComment] = React.useState('');
const [mutate, { status }] = useMutation(createComment, {
onMutate({ input }) {
// do something before the mutation run
return () => {
// rollback changes if the mutation failed
};
},
onSuccess({ data, input }) {
// do something once the mutation succeeded
},
onFailure({ error, rollback, input }) {
// do something once the mutation failed
},
onSettled({ status, error, data, rollback, input }) {
switch (status) {
case 'success': {
// do something if the mutation succeeded
}
case 'failure': {
// do something if the mutation failed
}
}
},
});
const handleSubmit = React.useCallback(
function handleSubmit(event) {
mutate({ authorId, comment });
},
[mutate, comment]
);
// render your UI
}
If you are using SWR, you can use useMutation
to run your mutations to perform Optimistic UI changes.
import { cache, mutate } from 'swr';
function createComment(input) {
// ...
}
function useCreateComment() {
return useMutation(createComment, {
onMutate({ input }) {
const oldData = cache.get('comment-list');
// optimistically update the data before your mutation is run
mutate('comment-list', current => current.concat(input), false);
return () => mutate('comment-list', oldData, false); // rollback if it failed
},
onFailure({ status, rollback }) {
if (status === 'failure' && rollback) rollback();
},
});
}
This way when you run mutate
, it will first optimistically update your SWR cache and if it fails it will rollback to the old data.
const [mutate, { status, data, error, reset }] = useMutation<
Input,
Data,
Error
>(mutationFn, {
onMutate,
onSuccess,
onFailure,
onSettled,
throwOnFailure,
useErrorBoundary,
});
const promise = mutate(input, {
onSuccess,
onSettled,
onError,
throwOnFailure,
});
Only if you are using TypeScript
Input = any
Data = any
Error = any
mutationFn(input: Input): Promise<Data>
onMutate?({ input: Input }): Promise<rollbackFn | undefined> | rollbackFn | undefined
onMutate
onSuccess?({ data: Data, input: Input }): Promise<void> | void
onFailure?({ error: Error, rollback: rollbackFn, input: Input }): Promise<void> | void
onSettled?({ status: 'success' | 'failure', error?: Error, data?: Data, rollback?: rollbackFn, input: Input}): Promise<void> | void
throwOnFailure?: boolean
true
, a failure in the mutation will cause the mutate
function to throw. Disabled by default.useErrorBoundary?: boolean
(default false)
true
, a failure in the mutation will cause the Hook to throw in render time, making error boundaries catch the error.mutate(input: Input, config: Omit<Options<Input, Data, Error>, 'onMutate' | 'useErrorBoundary'> = {}): Promise<Data | undefined>
status: 'idle' | 'running' | 'success' | 'failure'
idle
initial status of the hook, and the status after a resetrunning
if the mutation is currently runningsuccess
if the mutation resolved successfullyfailure
if the mutation failed to resolvedata: Data
error: Error
Author: sergiodxa
Source Code: https://github.com/sergiodxa/use-mutation
#react #reactjs #javascript