📌React-query란?
리액트쿼리는 리액트 앱의 서버 상태를 관리하는 라이브러로, 클라이언트에서 서버 데이터 캐시를 관리한다. 리액트 코드에 서버 데이터가 필요할 때, fetch나 axios를 사용해 서버로 바로 이동하지 않고 react-query 캐시를 요청한다. 또한 리액트쿼리 클라이언트를 어떻게 구성했느냐에 따라서 해당 캐시의 데이터를 유지 관리한다.
🛠프로젝트에 리액트쿼리 셋팅하기
- 리액트쿼리 라이브러리 추가
- 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
- 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>)}
</>
)