Hooks는 값의 재사용이 아니라 로직의 재사용을 위한 것이다!
커스텀훅도 마찬가지다. 훅을 호출하는 컴포넌트들마다 내부 상태들이 각각 다 다르게 설정되고, 호출하는 곳마다 네트워크 통신이 발생한다.
1. cache 안됨!
2. 네트워크 통신에 실패했을 때 재시동 할 수 있는 retry 기능이 없음!
React Query는 이러한 문제들을 쉽게 해결해준다!
- 네트워크 통신을 간편하게 할 수 있게 해줌!
- 로딩 중인지, 에러가 발생했는지 등을 쉽게 알 수 있음!
여러 컴포넌트에 걸쳐서 똑같은 데이터를 네트워크에 요청하는게 아니라 얼마동안 메모리상에 캐시를 해둘건지 설정할 수 있는 cache 시스템 제공!- global상태관리도 제공!
- 네트워크 통신에 실패했다면 재시동하는 retry 기능 제공!
네트워크 통신이나, 비동기적으로 데이터를 관리해야 하는 경우 유용하게 쓰인다!
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
const queryClient = new QueryClient()
function Example() {
const query = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
return (
<div>
{query.isLoading
? 'Loading...'
: query.isError
? 'Error!'
: query.data
? query.data.map((todo) => <div key={todo.id}>{todo.title}</div>)
: null}
</div>
)
}
function App () {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
이게 공식문서에 나와있는 기본 셋팅이다! 이제 이걸 내 코드에 적용해보면,
// Products.jsx
import React, { useState } from "react";
import { useQuery } from "react-query";
export default function Products() {
const [checked, setChecked] = useState(false);
const {
isLoading,
error,
data: products,
} = useQuery(["products"], async () => {
// 첫번째 인자는 key, 두번째 인자는 useQuery가 호출될 때 호출되는 함수
console.log("fetching...");
return fetch(`data/products.json`).then((res) => res.json());
});
// const [loading, error, products] = useProducts({ salesOnly: checked });
const handleChange = () => setChecked((prev) => !prev);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>{error}</p>;
return (
<>
...
</>
);
}
useProducts
를 사용하는 대신 useQuery
를 사용했다!
useQuery를 사용했더니
두 번씩 됐던 fetching이 이번엔 한번만 됐다!
그 이유는!
React Query를 사용하면 query별로 고유한 key를 제공한다.
그 key 아래에 데이터를 보관하는데, 현재 동일한 key의 데이터를 호출하고 있으므로 각각 fetch되는게 아니라 products라는 key의 데이터가 메모리상에 있으므로 캐시된 데이터를 불러오는 것이다!
정리하면 두 컴포넌트가 동일한 useQuery의 key를 사용하므로 딱 한번만 네트워크 통신이 발생한 것이다!