본문 바로가기

Web/React-query

React-query 시작하기

📌React-query란?

리액트쿼리는 리액트 앱의 서버 상태를 관리하는 라이브러로, 클라이언트에서 서버 데이터 캐시를 관리한다. 리액트 코드에 서버 데이터가 필요할 때, fetch나 axios를 사용해 서버로 바로 이동하지 않고 react-query 캐시를 요청한다. 또한 리액트쿼리 클라이언트를 어떻게 구성했느냐에 따라서 해당 캐시의 데이터를 유지 관리한다.


🛠프로젝트에 리액트쿼리 셋팅하기

  1. 리액트쿼리 라이브러리 추가
  2. queryClient 셋팅

         - 쿼리와서버의데이터캐시를관리하는클라이언트 

const queryClient = new QueryClient(); 
//클라이언트가 가지고 있는 캐시와 모든 기본 옵션을 provider의 자식 컴포넌트도 사용할 수 있게된다.
  • 최상위컴포넌트(App.jsx 또는 index.jsx)에 queryProvider 추가

자녀 컴포넌트에 캐시와 클라이언트 구성을 제공할 queryProvider 적용해야한다. queryProvider에 props로 넘겨줄 쿼리 클라이언트(바로 위에서 생성한 queryClient)를 client 속성에 넣어준다.

   <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>

이렇게 설정해주면 리액트쿼리를 앱 내에서 사용할 수 있게된다.


✅ React-query가 제공하는 Hooks 살펴보기 (+계속 업데이트할 예정)

1) useQuery(queryKey: string | [] , function, {option1, option2...})

서버에서 데이터를 가져오기위해 (서버에서 데이터를 가져오는 훅) 사용하는 훅이다. ( GET요청 )

첫번째 인수: '쿼리키'로 각각의 쿼리를 유니크한 쿼리가 되도록 구분해준다.

==> 나는 사실 이게 잘 와닿지 않았는데, 예를들어 사용자가 특정 게시물을 클릭할 때, 해당 게시물에 달린 댓글들을 불러와야한다고 치자.

const fetcher = async ({ queryKey }: { queryKey: string }) => {
  const response = await axios.get(queryKey, {
    withCredentials: true,
  });
  return response.data;
};

useQuery('comments' , 
	() => fetcher({ queryKey: `http://localhost:3095/api/comments/${postID}` }),
    {option1, option2...})

위와같이 Get요청을 날릴 때,  불러온 댓글 데이터들이 comments라는 이름으로 캐싱이된다.

이 상태에서 다른 포스트를 클릭할 경우, react-query는 comments 쿼리를 다시 실행하려다가 comments 라는 이름으로 이미 캐싱된 (이전에 본 포스트에서 불러온 comments라는 이름으로 캐싱된) 데이터가 있는 것을 보고, 새로운 요청없이 캐싱되어있는 이전 데이터들을 보여준다. 

쉽게말해 1번 포스트를 보다가 2번 포스트를 보게 될 경우, 2번 포스트에 1번 포스트의 댓글들이 그대로 뜬다는 의미이다.

이럴 경우 쿼리키를 각 포스트별로 유니크하게 구분될 수 있도록 배열 형태로 설정한다.

useQuery(['comments',postID] , 
	() => fetcher({ queryKey: `http://localhost:3095/api/comments/${postID}` }),
    {option1, option2...})

위와같이 수정을 하면 쿼리키는 comments라는 이름과 postID를 함께 확인하여 캐싱 데이터를 분별하고 fetcher를 실행한다.

두번째 인수: 쿼리에 대한 데이터를 가져오는 함수로 데이터를 가져오는 비동기 함수여야함,

세번째 인수: 옵션(리액트쿼리에서 제공하는 옵션들이 있다.) ex) staleTime: 캐시데이터를 업데이트하는 주기 아래 예시코드의 경우 2초마다 캐싱된 데이터가 최신인지 확인하고 업뎃한다.

const { isLoading, isError, data: userData } = useQuery("users", () => fetcher({ queryKey: "http://localhost:3095/api/users" }),{ staleTime: 2000 });

 

useQuery 옵션들

https://tanstack.com/query/v4/docs/reference/useQuery

 

useQuery | TanStack Query Docs

const { data,

tanstack.com

  • isLoading:데이터를 로딩하는 중인지 boolean, 쿼리 함수가 아직 해결되지 않았고 캐시된 데이터도 없는 상태(표시할 캐시 데이터가 없는) ,  쿼리를 만든 적이 없다는 의미(isFetching true이면서) ===> 캐시된 데이터를 고려
  • isError: 비동기 통신에서 오류가 것인지 boolean , 리액쿼리는 기본적으로 3번의 요청을 보내보고 해당 데이터를 가져올 있는지 없는지 결정한다.
  • isFetching:  isLoading보다 상위개념이고(isLoading isFetching 부분집합), 비동기 쿼리가 해결되지 않았음을 의미 (Fetching 완료하지 않은 상태) ===> 데이터를 가져오는 (isLoading상태이거나 isLoading 참인 경우) ===> 캐시된 데이터를 고려하지 않음

 

2) useMutation(function)

서버에 데이터를 업데이트 하도록 서버에 네트워크 호출을 실시한다( 다른 말로는 서버사이드 이펙트에 수행될 사용된다. ) useMutation 경우 업데이트 시도 실패시 재시도 하지 않는 것이 기본값이고 설정은 가능하다.

useMutation mutate함수를 반환한다. ==> useMutation에 작성한 내용을 mutate 함수를 통해 실행시킨다.

async function updatePost(postId, contents) {
  const response = await fetch(
    `https://localhost:3000/posts/${postId}`,
    { contents },
    {withCredentials:true}
  );
  return response.data;
}

const updateMutation = useMutation((postId) => updatePost(postId,contents));

 

return (
<>
  <button onClick={() => updateMutation.mutate(post.id)}> Update title </button>
  {updateMutation.isError && (<p style={{ color: "red" }}>Error updating the post...</p>)}
  {updateMutation.isLoading && (<p style={{ color: "purple" }}>Updating the post</p>)}
  {updateMutation.isSuccess && (<p style={{ color: "green" }}> Post has been updated</p>)}
</>
)