React.js

React Query 3편

Hoon1994 2022. 11. 14. 15:19

 

✏️ React Query의 Mutation, Infinite Query

 

지난 포스팅에서는 React Query의 Stale과 Cache에 대해서 알아보았다.

이번 포스팅에서는 Mutation, Infinite Query에 대해서 알아보려고 한다. 

 

 

Mutation

useQuery는 데이터를 조회하는 용도로 사용한다. 그러면 Post, Delete, Put 등의 HTTP Method를 사용하기 위해서는 어떻게 할까.

useMutation을 사용할 수 있다. useMutation와 useQuery는 비슷하지만 조금 다르다.

 

  • useMutation은 mutate 함수를 반환한다.
  • useQuery와 달리 Query Key 값이 필요하지 않다.
  • 캐시된 데이터가 없기 때문에 isLoading을 지원하지 않는다. 

 

기본적인 사용 방법은 아래와 같다.

async function deletePost(postId) {
  const response = await fetch(`https://jsonplaceholder.typicode.com/postId/${postId}`, { method: 'DELETE' });
  return response.json();
}

const deleteMutation = useMutation(deletePost);

  return (
    <>
      <button onClick={() => deleteMutation.mutate(post.id)}>Delete</button>
      {deleteMutation.isError && <p style={{ color: 'red' }}>Error deleting the post</p>}
      {deleteMutation.isLoading && <p style={{ color: 'purple' }}>Deleting the post</p>}
      {deleteMutation.isSuccess && <p style={{ color: 'green' }}>Post has(not) been deleted</p>}
    </>
  );

 

onSuccess, onError, onSettled를 통해 추가 로직을 작성할 수도 있다.

 

async function deletePost(postId) {
  const response = await fetch(`https://jsonplaceholder.typicode.com/postId/${postId}`, { method: 'DELETE' });
  return response.json();
}

const deleteMutation = useMutation(deletePost, {
    onSuccess: () => {...},
    onError: () => {...},
    onSettled: () => {...}
});

 

  • onSuccess : 요청이 성공한 경우 
  • onError: 에러가 발생한 경우
  • onSettled: 성공, 실패 상관 없이 마지막에 무조건 실행

 

 

Infinite Query

 

React Query에서 지원해주는 useInfiniteQuery 함수를 통해 무한 스크롤을 구현할 수 있다.
useInfiniteQuery를 요악하여 설명하자면 아래와 같다.

 

  • 다음에 실행할 쿼리를 추적한다.
  • 반환되는 값은 2가지 종류가 있다.
    • pages: 하나의 useQuery에서 반환되는 값이 배열로 이루어져있음.
    • pageParam: 각 page의 매개변수가 저장되어 있음.
  • getNextPageParam, getPreviousPageParam으로 다음 API를 호출할 때 사용할 값을 지정할 수 있음. 
  • fetchNextPage, fetchPreviousPage 함수를 통해 사용자의 액션에 따라 API를 호출할 수 있음 

 

간단한 사용 예시다. react-infinite-scroller 라이브러리를 함께 사용하였다.

const fetchUrl = async (url) => {
  const response = await fetch(url);
  return response.json();
};

export function InfiniteSpecies() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isError,
    error,
    isLoading,
    isFetching,
  } = useInfiniteQuery(
    "sw-species",
    ({ pageParam = initialUrl }) => fetchUrl(pageParam),
    {
      getNextPageParam: (lastPage) => lastPage.next || undefined,
    }
  );

  if (isLoading) return <div className="loading">...loading</div>;
  if (isError) return <div>{error.toString()}</div>;

  return (
    <>
      {isFetching && <div className="loading">...loading</div>}
      <InfiniteScroll loadMore={fetchNextPage} hasMore={hasNextPage}>
        {data.pages.map((pageData) => {
          return pageData.results.map((specie) => {
            return <Species key={specie.name} {...specie} />;
          });
        })}
      </InfiniteScroll>
    </>
  );

 

useInfiniteQuery는 첫번째 인자로 key를 받고, 두번째 인자로는 queryFn을 받는다. 세번째는 옵션 값이다.

최초 컴포넌트가 렌더링될 때는 전달되는 값이 없기 때문에 initialUrl을 기본 값으로 설정하였다. 
이후에는 getNextPageParam 함수를 통해 전달되는 값이 정해지고, 만약 API Response에서 next에 값이 존재하지 않을 경우 
undefined로 설정되어 더 이상 호출할 데이터가 없음을 알려준다. 

그렇게되면 hasNextPage 값이 변경되고, 더 이상 데이터가 호출되지 않게된다. 

react-infinite-scroller를 꼭 사용해야 하는 건 아니나, useInfiniteQuery와 호환이 잘 된다.