I'm just playing around with react-query
with typescript
I mean I do my first attempts
Is it the right way?
const useCreateTodo = () => {
const queryClient = useQueryClient();
return useMutation(
(todo: TodoDto) => axios.post(`${URL}/todos`, todo).then((res) => res.data),
{
onMutate: async (newTodo: TodoDto) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries("todos");
// Snapshot the previous value
const previousTodos = queryClient.getQueryData("todos");
// Optimistically update to the new value
queryClient.setQueryData<TodoDto[] | undefined>("todos", (old) =>
old ? [...old, newTodo] : old
);
// Return a context object with the snapshotted value
return { previousTodos };
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (
err,
newTodo,
context:
| {
previousTodos: unknown;
}
| undefined
) => {
queryClient.setQueryData(
"todos",
context ? context.previousTodos : context
);
},
// Always refetch after error or success:
onSettled: () => {
queryClient.invalidateQueries("todos");
},
}
);
};
optimistic updates are a bit tricky for type inference. There is now an example for this exact case in the docs.
From that example:
const addTodoMutation = useMutation(
newTodo => axios.post('/api/data', { text: newTodo }),
{
// When mutate is called:
onMutate: async (newTodo: string) => {
setText('')
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries('todos')
// Snapshot the previous value
const previousTodos = queryClient.getQueryData<Todos>('todos')
// Optimistically update to the new value
if (previousTodos) {
queryClient.setQueryData<Todos>('todos', {
...previousTodos,
items: [
...previousTodos.items,
{ id: Math.random().toString(), text: newTodo },
],
})
}
return { previousTodos }
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (err, variables, context) => {
if (context?.previousTodos) {
queryClient.setQueryData<Todos>('todos', context.previousTodos)
}
},
// Always refetch after error or success:
onSettled: () => {
queryClient.invalidateQueries('todos')
},
}
)
A couple of explanations:
onMutate, so that type inference will work for the mutateFn (newTodo is inferred) as well as for the context in onError.getQueryData so that previousTodos is typed. You don't need to union with undefined though - react-query will do that for you.setQueryData is tricky, because it requires you to return an Array, but old can be undefined. I prefer to use the previousTodos returned by getQueryData
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With