React Query에서는 mutation이 완료되기 전에 UI를 최적화하는 두 가지 방법을 제공한다.
🚩 주의
- useMutation의 mutationFn이 비동기 작업을 수행한다면, mutationFn를 async/await로 처리해야 onSettled가 mutationFn의 처리가 완료된 후에 실행된다.
- 그렇지 않으면 mutationFn 처리 전에 onSettled가 실행되어 정상적으로 작동하지 않을 가능성이 있다.
// 🚩 Updating a list of todos when adding a new todo
const queryClient = useQueryClient()
useMutation({
mutationFn: updateTodo,
onMutate: async (newTodo) => {
await queryClient.cancelQueries({ queryKey: ['todos'] })
const previousTodos = queryClient.getQueryData(['todos'])
queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
return { previousTodos }
},
onError: (err, newTodo, context) => {
queryClient.setQueryData(['todos'], context.previousTodos)
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
// 🚩 Updating a single todo
useMutation({
mutationFn: updateTodo,
onMutate: async (newTodo) => {
await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] })
const previousTodo = queryClient.getQueryData(['todos', newTodo.id])
queryClient.setQueryData(['todos', newTodo.id], newTodo)
return { previousTodo, newTodo }
},
onError: (err, newTodo, context) => {
queryClient.setQueryData(
['todos', context.newTodo.id],
context.previousTodo,
)
},
onSettled: (newTodo) => {
queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] })
},
})
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: async () => {
return await queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
const { isPending, submittedAt, variables, mutate, isError } = addTodoMutation
<ul>
{todoQuery.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{isPending && <li style={{ opacity: 0.5 }}>{variables}</li>}
{isError && (
<li style={{ color: 'red' }}>
{variables}
<button onClick={() => mutate(variables)}>Retry</button>
</li>
)
}
</ul>