React Query mutation

이재철·2023년 3월 4일
0

react-query

목록 보기
10/12
post-thumbnail

What are mutations?

mutations 은 부과 효과(side effect) 함수 입니다.
react query로 서버 상태를 관리하기 때문에 mutations 은 서버에 부가 효과를 일으킬 수 있습니다.

Similarities to useQuery

useMutationuseQuery 가 쿼리에 대해 수행하는 것처럼 mutation 의 상태를 추적합니다.

loading , error, status fields 를 제공하여 무슨 상태인지 알 수 있습니다.

Differences to useQuery

대부분의 쿼리는 자동으로 실행됩니다.

  • mutations 의 경우 자동으로 실행되지 않습니다.
  • mutationsuseQuery 처럼 상태를 공유하지 않습니다.
  • 컴포넌트에서 useQuery 를 여러 번 호출 할 수 있으며 캐시된 결과를 동일하게 반환하지만 mutations 은 동작하지 않습니다.

Tying mutations to queries

mutation 은 대부분 쿼리에 직접 연결되지 않게 설계합니다.
mutation 이 쿼리에 대한 변경 사항을 반영하기 위해 두 가지 방법이 있습니다.

Invalidation (무효화)

화면을 최신 상태로 만드는 가장 간단한 방법입니다.
react query가 현재 사용 중인 경우 해당 데이터를 다시 가져오며, 가져오기가 완료되면 화면이 자동으로 업데이트 됩니다.
➡ 무효화할 쿼리만 라이브러리에 알려주면 됩니다.

💡 queryClient.invalidateQueries(queryKey) 를 통해 캐시를 무효화 할 수 있습니다.

const useUpdatePostMutation = (postId: number) => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({
      postId,
      title,
      body,
    }: {
      postId: number;
      title: string;
      body: string;
    }) => {
      return updatePostDetailApi(postId, title, body);
    },
    onSuccess: (newPost) => {
      // ✅ refetch the comments list for our blog post
      queryClient.invalidateQueries(queries.posts.detail(postId).queryKey);

    },
  });
};

Direct updates

mutation 가 알아야 할 모든 정보를 반환하는 경우 데이터를 다시 검색할 필요가 없습니다.

💡 setQueryData 를 통해 직접 쿼리 캐시를 업데이트 할 수 있습니다.

const useUpdatePostMutation = (postId: number) => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({
      postId,
      title,
      body,
    }: {
      postId: number;
      title: string;
      body: string;
    }) => {
      return updatePostDetailApi(postId, title, body);
    },
    onSuccess: (newPost) => {
      // ✅ update detail view directly
      queryClient.setQueryData(queries.posts.detail(postId).queryKey, newPost);
    },
  });
};

setQueryData 를 통해 직접 캐시에 데이터를 주입하면 데이터가 백엔드에서 반환된 것 처럼 동작합니다.
➡ 해당 쿼리를 사용하는 모든 컴포넌트가 다시 렌더링되는 것을 뜻합니다.

🤟 대부분 무효가 선호되는 것이 안전한 접근법 이라고 생각합니다.

Optimistic updates (낙관적 업데이트)

useQuery 캐시는 쿼리 간 전환 시, prefetching 과 결합할 때 데이터를 즉시 제공합니다.
➡ 전체적인 UI가 매우 빠르게 느껴집니다.
mutations 도 같은 이 점을 어떻게 얻을 수 있을까요?

낙관적인 업데이트는 서버에 전송하기도 전에 mutations 의 성공을 가장합니다.
1. 성공적인 응답을 받음
2. 실제 데이터를 보기 위해 무효화 처리
3. 요청이 실패하면 UI를 변환 전 상태로 롤백 처리

언제 사용 할까?

구체적인 예시는 공식 문서에 정리되어 있습니다. ( typescript example )

낙관적 업데이트는 정말 필요할 때 사용해 주세요
➡ UX를 실제로 나쁘게 만들 수 있으며, 로딩 애니메이션을 보여주는 것이 더 효율적일 수 있습니다.

Common Gotchas

확실하지 않을 수 있는 mutations 을 사용할 때 몇 가지 좋은 사항에 대해서 알아봅시다.

awaited Promises

mutation 콜백에서 반환된 Promise는 react query에 의해 대기(awaited)되며, 실제로 invalidateQueries 는 Promise를 반환합니다.
➡ 관련 쿼리가 업데이트되는 동안 변환을 loading 상태로 유지하려면 콜백에서 invalidateQueries 의 결과를 반환해야 합니다.

{
  // 🎉 will wait for query invalidation to finish
  onSuccess: () => {
    return queryClient.invalidateQueries(['posts', id, 'comments'])
  }
}
{
  // 🚀 fire and forget - will not wait
  onSuccess: () => {
    queryClient.invalidateQueries(['posts', id, 'comments'])
  }
}

Mutate or MutateAsync

  • mutate 는 아무것도 반환하지 않습니다.
    -mutateAsync 는 돌연변이의 결과를 포함하는 Promise를 반환합니다.

mutation reponse 에 대한 접근이 필요할 때 mutateAsync 를 사용 할 수 있습니다.

항상 mutate 을 해야한다고 생각합니다! 왜?

  • mutate 는 콜백을 통해 데이터나 오류에 액세스할 수 있으며 오류 처리 걱정이 없음
    ➡ react query는 내부적으로 오류를 캐치하거나 무시하기에 mutate 를 사용할 때 오류 처리가 필요 없습니다. 🤟
  • mutateAsync 는 Promise를 제어할 수 있기 때문에 오류를 직접 처리해야함

그럼 MutateAsync 언제 쓰나요?

  • Promise가 꼭 필요할 경우
  • 여러 mutations를 동시에 시작하며, 모든 mutations 이 끝나기를 기다리는 작업
  • 콜백 지옥에 빠질 수 있는 의존적 mutations 이 있는 경우 필요함

Mutations only take argument for variables

반환하는 마지막 인수가 옵션 객체이므로 useMutation 은 현재 변수에 대해 하나의 인수만 사!
용할 수 있습니다.
➡ 제한적이지만, 객체를 사용하여 쉽게 해결

// 🚨 this is invalid syntax and will NOT work
const mutation = useMutation((title, body) => updateTodo(title, body))
mutation.mutate('hello', 'world')

// ✅ use an object for multiple variables
const mutation = useMutation(({ title, body }) => updateTodo(title, body))
mutation.mutate({ title: 'hello', body: 'world' })

Some callbacks might not fire

useMutation 에 대한 콜백뿐만 아닌 mutate 에 대한 콜백을 가질 수 있습니다.
mutate 의 콜백 전에 useMutation 이 호출되는 것을 알아야 합니다.

🚨 변환이 완료되기 전에 컴포넌트가 마운트 해제 된 경우 mutate 콜백이 전혀 실행되지 않을 수 있습니다.
➡ 콜백에서 문제를 분리하는 것이 좋다고 생각합니다.

useMutation 콜백 수행
절대적으로 필요한 작업 및 로직 관련 작업(쿼리 무효화)
➡ 무효화 논리는 항상 동일하기 때문에 Custom hook의 재사용성이 높습니다.

리디렉션, 토스트 알림을 콜백으로 표시하는 것과 같은 UI관련 작업을 수행
➡ 사용자가 변환이 완료되기 전에 현재 화면에서 멀리 탐색한 경우, 의도적으로 작동하지 않음

const useUpdateTodo = () =>
  useMutation(updateTodo, {
    // ✅ always invalidate the todo list
    onSuccess: () => {
      queryClient.invalidateQueries(['todos', 'list'])
    },
  })

// in the component

const updateTodo = useUpdateTodo()
updateTodo.mutate(
  { title: 'newTitle' },
  // ✅ only redirect if we're still on the detail page
  // when the mutation finishes
  { onSuccess: () => history.push('/todos') }
)

참조 링크

profile
혼신의 힘을 다하다 🤷‍♂️

0개의 댓글