react-query

조승윤·2022년 9월 23일

react-query를 사용하게 된 이유

각 페이지에서 공통적으로 사용하는 전역 상태 데이터를 주로 redux를 이용해 처리했다. 프로젝트가 작을때는 괜찮았지만 프로젝트가 점점 커짐에 따라 관리해야할 데이터가 많아져 너무 복잡해졌다.
또한 프론트엔드 개발에서는 api통신을 빼놓을 수가 없는데 비동기 데이터관리가 용이해 사용하게 되었다.

사용해보면서 느낀 장점

✔️데이터 캐싱처리가 편해졌다.
✔️에러핸들링이 더욱 편해졌다.
✔️여러 컴포넌트에서 동시에 데이터가 필요한 경우 처리가 편해졋다
✔️구조가 redux를 사용할때보다 단순해졌다.
✔️직접 만들어서 쓰던 기능들이 react-query에서는 옵션으로 지원하기 때문에 사용성이 좋았다.
✔️infinity query 기능을 지원해 infinity scroll을 캐싱처리를 하면서 간단하게 구현이 가능했다.

react-query를 사용하기전에는 redux에 의존했다. react-query를 사용한 뒤
Redux로는 전역 상태 관리를하고 react-query로는 서버에서 받아온 데이터를 관리를 하게 되었다.

사용예시

const UserWallet = () => {
  
    const { isLoading, isError, data: Get_Wallet_list, error } = useQuery(["walletList", page], 
    async () => {
      const { data } = await axios.get(process.env.REACT_APP_SERVER_URL + `/wallet`);
      return data;
    },
    {
      staleTime: 1000 * 60 * 60,
      cacheTime: 1000 * 60 * 60,
      refetchOnWindowFocus: false,
      placeholderData: {},
      retry: 0,
      onSuccess: (data) => {
        console.log(data);
      },
      onError: (e) => {
        console.log(e.message);
      },
    }
  );
    
  if (isLoading) return <div> 로딩중... </div>;
  if (error) return <div> 에러: {error.message} </div>;
  
  return (생략);
}

Lifecycle과 Refetching

"walletList", page 쿼리 인스턴스가 mount ->
데이터 fetch 후 "walletList", page라는 query key로 캐싱 ->
데이터는 fresh 상태에서 staleTime(기본값 0) 이후 stale 상태로 변경 ->
"walletList", page 쿼리 인스턴스가 unmount ->
캐시는 cacheTime(기본값 5min) 만큼 유지되다 가비지 콜렉터로 수집 ->
cacheTime이 지나기 전에 "walletList", page 쿼리 인스턴스가 mount되면 fetch가 실행되고 fresh 값을 가져오는 동안 캐시 데이터를 보여줌

https://tanstack.com/query/v4/docs/guides/caching?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/caching#basic-example

Refetching이 되는 경우

✔️브라우저에 focus가 되었을 경우 (refetchOnWindowFocus)
✔️새로 mount가 되었을 경우 (refetchOnMount)
✔️네트워크가 끊어졌다가 다시 연결된 경우 (refetchOnReconnect)
✔️React-Query 는 캐싱 된 데이터는 항상 stale 하다고 판단하며, stale 상태인 데이터를 Refetching 한다.

리턴 데이터

data

  • 쿼리 함수가 리턴한 Promise에서 resolve된 데이터

isLoading

  • 저장된 캐시가 없는 상태에서 데이터를 요청중일 때 true

isFetching

  • 캐시가 있거나 없거나 데이터가 요청중일 때 true

옵션

staleTime

  • React-Query 는 데이터를 fetching 해온 후 데이터를 캐싱 하며 데이터가 stale 하다고 판단될 때 데이터를 refetching 하게 된다.

  • 데이터가 fresh 에서 stale 상태로 변경되는데 걸리는 시간이 staleTime타임이다.

cacheTime

  • 쿼리 인스턴스가 unmount 되면 inactive 상태로 변경된다. 데이터가 inactive 상태일 때 캐싱된 상태로 남아있는 시간이다.

refetchOnWindowFocus

  • react-query는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행하는데 그 재실행 여부 옵션이다.

placeholderData

  • initialData 옵션과 비슷하게 이미 데이터가 있는것처럼 작동할 수 있다 하지만 데이터는 캐시에 유지되지 않는다
  • fetch하는 동안 컨텐츠를 빨리 표시하는데 유용하다.

retry

  • 재호출 몇번 할지 설정하는 옵션이다.

onSuccess

  • 성공시 호출

onError

  • 실패시 호출

옵션은 너무 많아 자주 사용하는것만 적어 놓았다 자세한 옵션은 공식문서를 참고하자

queryKey

queryKey는 문자열과 배열을 넣을 수 있다. 쿼리 키는 캐싱처리를 쉽게 만들어준다. 쿼리 키가 다르면 캐싱도 별개이기 때문이다.
나는 쿼리 키를 이용해 페이징이나 검색 옵션등을 주로 처리했다.

useQuery(["walletList", 1], ...)
useQuery(["walletList", 2], ...)

이런식으로 "walletList", page를 Key로 사용하여 데이터를 캐싱 할 수가 있다.
다른 컴포넌트에서 "walletList", page를 Key로 사용한 useQuery Hook이 있다면 캐시된 데이터를 사용한다.

useMutation

서버 데이터를 가져오는 것은 reactive하게 동작하는 useQuery를 사용하면 되겠지만, 서버 데이터 업데이트는 그런 방식으로 사용하기에는 적절하지 않다.
useQuery와는 다르게 create, update, delete(생성/수정/삭제) 에는 useMutation 을 사용한다

import { useMutation } from "react-query";

  const loginMutation = useMutation(Api, {
    onMutate: variable => {
      console.log("onMutate", variable);
      // variable : {Id: '', Password: ''}
    },
    onError: (error, variable, context) => {
    },
    onSuccess: (data, variables, context) => {
    },
    onSettled: () => {
      console.log("완료");
    }
  });

  const handle = () => {
    loginMutation.mutate({ Id: id, Password:password });
  };

useQuery와 동일하게 onSuccess, onError, onSettled 콜백을 전달할 수 있으며 거기에 더해 mutate를 호출했을 때 실행할 onMutate 콜백도 사용할 수 있다.

onMutate

  • mutation 함수가 실행되기 전에 실행된다 함수가 받을 동일한 변수가 전달된다.
  • useMutation을 정의 해둔 뒤 이벤트가 발생되었을 때 mutate를 사용하면 된다.

onSettled

  • 성공한 데이터 또는 error가 전달될 때 실행된다.

useMutation공식문서

update후 다시 데이터를 get하고싶다면 성공 후 invalidateQueries를 해주면된다.

invalidateQueries

글 작성이나 댓글등을 작성하면 서버에서 댓글 목록을 다시 Get 해서 리스트를 받아와야 한다.
이와 같은 경우에는 staleTime이 지나기 전에 직접 쿼리를 무효화해서 새로운 데이터를 가져온다.

const mutation = useMutation(newTodo => axios.post('process.env.REACT_APP_SERVER_URL+/wallet', ...))
const queryClient = useQueryClient();

queryClient.invalidateQueries(); //모든 쿼리를 무효화한다.


queryClient.invalidateQueries(["walletList"]) //walletList로 시작하는 모든 쿼리를 무효화한다. ex) ["walletList", 1], ["walletList", 2], ...


queryClient.invalidateQueries(["walletList", 2]) // ['walletList', 1] 키를 가진 쿼리를 무효화한다.

0개의 댓글