Mutation을 수행하기 전에 상태를 낙관적으로(optimistically) 업데이트하면 mutation이 실패할 가능성이 있습니다. 이러한 실패 사례의 대부분은 낙관적 쿼리에 대한 refetch를 트리거하여 실제 서버 상태로 되돌릴 수 있습니다. 하지만 일부 상황에서는 refetching이 제대로 작동하지 않을 수 있으며, mutation 오류는 refetching이 불가능한 서버 문제를 나타낼 수 있습니다. 이 경우 대신 업데이트를 롤백하도록 선택할 수 있습니다.
이를 위해 useMutation
의 onMutate
핸들러 옵션을 사용하면 나중에 onError
및 onSettled
핸들러에 마지막 아규먼트로 전달될 값을 리턴할 수 있습니다. 대부분의 경우 롤백 함수를 전달하는 것이 가장 유용합니다.
const queryClient = useQueryClient()
useMutation({
mutationFn: updateTodo,
// When mutate is called:
onMutate: async (newTodo) => {
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await queryClient.cancelQueries({ queryKey: ['todos'] })
// Snapshot the previous value
const previousTodos = queryClient.getQueryData(['todos'])
// Optimistically update to the new value
queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
// 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) => {
queryClient.setQueryData(['todos'], context.previousTodos)
},
// Always refetch after error or success:
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
useMutation({
mutationFn: updateTodo,
// When mutate is called:
onMutate: async (newTodo) => {
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] })
// Snapshot the previous value
const previousTodo = queryClient.getQueryData(['todos', newTodo.id])
// Optimistically update to the new value
queryClient.setQueryData(['todos', newTodo.id], newTodo)
// Return a context with the previous and new todo
return { previousTodo, newTodo }
},
// If the mutation fails, use the context we returned above
onError: (err, newTodo, context) => {
queryClient.setQueryData(
['todos', context.newTodo.id],
context.previousTodo,
)
},
// Always refetch after error or success:
onSettled: (newTodo) => {
queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] })
},
})
원하는 경우 별도의 onError
및 onSuccess
핸들러 대신 onSettled
함수를 사용할 수도 있습니다:
useMutation({
mutationFn: updateTodo,
// ...
onSettled: (newTodo, error, variables, context) => {
if (error) {
// do something
}
},
})
Reference