react query optimistic update

euneun·2022년 2월 4일
4
post-thumbnail

서론

인스타에서 좋아요를 누를 때와 같이, 서버에 반영되기도전에 사용자에게 이미 반영되어있는것처럼 보이게 할 수 있는 동작을 구현할때 쓰이는 optimistic update의 사용법에 대해서 정리해보려고 한다

Optimistic update

리액트 쿼리에서는 이 기능을 optimistic update라고 하는데 한국어로 직역하면 낙관적 업데이트(?)라는 말이되어서 와닿지가 않는다.

'어차피 나중에 디비에 반영될거 우선 낙관적으로, 긍정적으로 서버 요청이 성공한다고 가정하고 하트부터 칠해놓고 보자~' 이런느낌으로 해석하면 될 것 같다.
(만약 서버오류시 다시 롤백)

사용법

  const addTodoMutation = useMutation(
    newTodo => axios.post('/api/data', { text: newTodo }),
    {
      // When mutate is called:
      onMutate: async (newTodo: string) => {
        setText('')
        // Cancel any outgoing refetches (so they don't overwrite our optimistic update)
        await queryClient.cancelQueries('todos')

        // Snapshot the previous value
        const previousTodos = queryClient.getQueryData<Todos>('todos')

        // Optimistically update to the new value
        if (previousTodos) {
          queryClient.setQueryData<Todos>('todos', {
            ...previousTodos,
            items: [
              ...previousTodos.items,
              { id: Math.random().toString(), text: newTodo },
            ],
          })
        }

        return { previousTodos }
      },
      // If the mutation fails, use the context returned from onMutate to roll back
      onError: (err, variables, context) => {
        if (context?.previousTodos) {
          queryClient.setQueryData<Todos>('todos', context.previousTodos)
        }
      },
      // Always refetch after error or success:
      onSettled: () => {
        queryClient.invalidateQueries('todos')
      },
    }
  )

위의 공식문서의 코드를 보면 이해가 갈텐데,

react-query의 useMutation을 쓸때
onMutate이라는 option에서
mutation을 실행하기 전에 수행해야할 것들에 대한것들을 명시할 수 있다!

여기서 우리가 실제 서버에 좋아요 요청을 보내기 전에,
캐시된 데이터의 좋아요 여부를 바꿔놓으면 되는 것이다!!

  1. queryClient.cancelQueries 수행 - 참고
    : 우선 명시해준 쿼리 키에 해당하는 쿼리들의 리페치들을 막는다 (우리가 onMutate에서 수행하는것들을 덮어쓰지 않게 하기위해서)
  2. queryClient.getQueryData 수행 - 참고
    : 이전 쿼리의 캐시된 데이터를 알아낸다
  3. queryClient.setQueryData 수행 - 참고
    : setQueryData로 이전 쿼리 값에서 원하는 부분을 변경해준다.
  4. onError에서도 setQueryData로 이전 값으로 변경해준다
    : 에러가 발생해서 실제 디비에 반영되지 못하는 경우도 생기므로, 롤백해주는 과정도 필요하다!

타입스크립트 문제

나는 타입스크립트 환경에서 사용하였는데, setQueryData에서 updater함수를 지정해줄때
타입이 제대로 추론되지 않아서 unknown, any로 받아야 했던 경우가 답답했다.

그런데 updater함수는 결국 이전 값을 필요로하니, getQueryData에 제네릭 타입을 추가하여 가져온 값을 사용하면 타입 문제를 해결할 수 있지 않을까라고 생각했다.

ex)

const 캐시된쿼리데이터 = queryClient.getQueryData<쿼리데이터의 타입>>(쿼리 키)

그리고 실제로 스택오버플로우에서도 비슷한 글을 찾을 수 있었다.

setQueryData에는 array를 반환해야하는데, setQueryData의 adapter 함수를 넣어줄때
파라미터가 undefined로 되기 쉬워서 까다로우니, getQueryData로 부터 온 데이터를 인자로 사용하는것이 더 선호된다고 한다

그리고 onMutate에 지정한 함수에서 리턴한 값이 onErroronSettled로 전해진다고 한다!

그래서 onMutate에서 값을 변경하기 전의 쿼리데이터를 리턴해준다면, onError에서 이전 값으로 롤백하기에 유용하다!!
🤩

profile
제대로 짚고 넘어가자!🧐

0개의 댓글