[TanStakQuery] Optimistic Updates

Jeris·2023년 5월 22일
0

Mutation을 수행하기 전에 상태를 낙관적으로(optimistically) 업데이트하면 mutation이 실패할 가능성이 있습니다. 이러한 실패 사례의 대부분은 낙관적 쿼리에 대한 refetch를 트리거하여 실제 서버 상태로 되돌릴 수 있습니다. 하지만 일부 상황에서는 refetching이 제대로 작동하지 않을 수 있으며, mutation 오류는 refetching이 불가능한 서버 문제를 나타낼 수 있습니다. 이 경우 대신 업데이트를 롤백하도록 선택할 수 있습니다.

이를 위해 useMutationonMutate 핸들러 옵션을 사용하면 나중에 onErroronSettled 핸들러에 마지막 아규먼트로 전달될 값을 리턴할 수 있습니다. 대부분의 경우 롤백 함수를 전달하는 것이 가장 유용합니다.

새 할 일을 추가할 때 할 일 목록 업데이트하기(Updating a list of todos when adding a new todo

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'] })
  },
})

Updating a single todo

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] })
  },
})

원하는 경우 별도의 onErroronSuccess 핸들러 대신 onSettled 함수를 사용할 수도 있습니다:

useMutation({
  mutationFn: updateTodo,
  // ...
  onSettled: (newTodo, error, variables, context) => {
    if (error) {
      // do something
    }
  },
})

Reference

profile
job's done

0개의 댓글