React Query - Optimistic Updates

소이뎁·2024년 7월 2일
1

React Query

목록 보기
1/1

React Query에서는 mutation이 완료되기 전에 UI를 최적화하는 두 가지 방법을 제공한다.

🚩 주의

  • useMutation의 mutationFn이 비동기 작업을 수행한다면, mutationFn를 async/await로 처리해야 onSettled가 mutationFn의 처리가 완료된 후에 실행된다.
  • 그렇지 않으면 mutationFn 처리 전에 onSettled가 실행되어 정상적으로 작동하지 않을 가능성이 있다.

onMutate 옵션 사용

  • onMutate 콜백 함수는 mutation이 실행되기 전에 호출된다.
  • 이 함수 내에서 queryClient.setQueryData를 사용하여 캐시 데이터를 직접 업데이트할 수 있다.
  • 이를 통해 UI가 즉시 업데이트되어 사용자에게 더 나은 경험을 제공할 수 있다.
  • 즉, 캐시 데이터 업데이트 -> UI에 반영
// 🚩 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] })
  },
})

useMutation 결과에서 반환된 variables 사용

  • useMutation 훅은 variables 속성(가장 최근 mutation 함수에 전달된 인수 값)을 반환한다.
  • 이 variables 값을 사용하여 UI를 직접 업데이트할 수 있다.
  • 이 방식은 onMutate 옵션을 사용하는 것과 유사하지만, 캐시 업데이트 대신 UI 상태를 직접 업데이트한다.
  • 즉, JSX에서 isPending, isError 등의 조건에 따라 variables를 사용하여 UI 직접 업데이트
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>

0개의 댓글