Optimistic Updates | TanStack Query Docs
Optimistic Update(낙관적 업데이트)
란 서버 업데이트 시 UI에서도 어차피 업데이트할 것이라고(낙관적인) 가정해서 미리 UI를 업데이트
시켜주고 서버를 통해 검증을 받고 업데이트 또는 롤백하는 방식이다.
즉, 변이가 성공하기를 희망하지만 실패하더라도 롤백할 수 있다는 의미이다.( ex) 페이스북 좋아요. )
장점으로는 서버 응답 받기 전 캐시가 먼저 업데이트 되기 때문에 사용자에게 빠른 경험을 줄 수 있다.
단점으로는 서버 응답이 실패 했을 경우 롤백 로직을 작성해야하기 때문에 코드가 복잡해진다.
onMutate콜백은 context값을 반환하고 onError 핸들러가 이 context값을 인수로 받아 캐시값을 이전 값으로 복원한다.
캐시를 업데이트할 데이터를 포함하는 특정 쿼리에서 onMutate함수는 진행 중인 모든 refetch를 취소한다.
**useMutation
**의 onMutate
핸들러 옵션을 사용하면 나중에onError
및 **onSettled
**핸들러 모두에 마지막 인수로 전달될 값을 반환할 수 있습니다 . 대부분의 경우 롤백 기능을 전달하는 것이 가장 유용하다.
const queryClient = useQueryClient()
useMutation({
mutationFn: updateTodo,
// mutate가 호출되는 경우
onMutate: async (newTodo) => {
// 다시 가져오기 취소
// 서버로 오는 데이터가 긍정적인 업데이트를 덮어쓰는 일이 없도록 해줌
await queryClient.cancelQueries({ queryKey: ['todos'] })
// Snapshot the previous value 이전값의 스냅샷
const previousTodos = queryClient.getQueryData(['todos'])
// 낙관적인 새 값으로 업데이트 합니다.
queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
// 스냅샷된 값으로 컨텍스트 객체를 반환합니다.
return { previousTodos }
},
// mutation이 실패하는 경우
// onMutate에서 반환된 컨텍스트를 사용해 롤백합니다.
onError: (err, newTodo, context) => {
queryClient.setQueryData(['todos'], context.previousTodos)
},
// 오류나 성공 후에는 항상 다시 가져옵니다.
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
useMutation({
mutationFn: updateTodo,
// mutate가 호출되는 경우
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
}
},
})