
애플리케이션의 데이터를 관리해준다.
설치
npm i react-query
import { QueryClient, QueryClientProvider } from 'react-query';
const client = new QueryClient();
root.render(
<React.StrictMode>
<QueryClientProvider client={client}>
<RouterProvider router={router} />
</QueryClientProvider>
</React.StrictMode>
);
위와 같이 App을 감싸주면 사용하기 위한 준비가 끝난 것이다.
const {data, isLoading} = useQuery(["movie", "now_playing"], getMovies);
// in api.ts
const BASE_URL = 'https://api.themoviedb.org/3';
const API_KEY = "c610b9320ba06d7f9a12ef4ed959de27";
export const getMovies = () => {
return fetch(`${BASE_URL}/movie/now_playing?api_key=${API_KEY}`).then(response => response.json());
}
const {data, isLoading} data와 isLoading은 검색된 data와 검색중인지 여부를 의미한다. 이 변수들로 화면을 구성할 수 있다.
useQuery(["movie", "now_playing"], getMovies);의 첫번째 인자는 key에 해당하고 unique해야 하며 배열이 아니라 문자열만 지정해도 된다. 배열로 지정하면 배열을 모두 이어붙인 값이 키가 된다.
두번째 인자는 실제 조회를 수행하는 함수이며 이 함수는 Promise를 리턴해주어야 한다.
useQuery를 하나이상 사용하면 data, isLoading이 변수명이 충돌이 발생한다. 이 때는 이름을 다르게 부여하여 충돌을 피한다.
const { isLoading: isInfoLoading, data: infoData } = useQuery(["info", coinId], () => fetchCoinInfo(`${coinId}`));
const { isLoading: isTickerLoading, data: tickersData }
= useQuery<TickerData>(["tickers", coinId], () => fetchTickersData(`${coinId}`) );
데이터 패칭용으로 사용되며 GET을 처리한다.
const { data, error, isFetching, isLoading, isSuccess, refetch } = useQuery(
queryKey, // 유니크한 키
queryFunction, // 실제 조회하는 함수로 promise를 반환, fetch, axios등의 함수를 사용
options // 옵션을 지정함.
)
주요 리턴값은 다음과 같다. 이 외에더 더 있으니 공식문서를 참조하자.
import { useQuery } from 'react-query';
// 데이터 패칭 함수
const getUserData(userId) = () => {
return fetch(`/api/user/${userId}`).then((response) => response.json());
}
const UserProfile = ({ userId }) => {
// useQuery 를 사용하여 데이터를 가져옵니다. ['user', userId] 는 쿼리 키를 의미합니다.
const { data, isError, error, isLoading } = useQuery(['user', userId], () => fetchUserData(userId));
if (isLoading) { // isLoading을 사용하여 데이터가 로딩중일 때 화면을 랜더링합니다.
return <div>Loading...</div>;
}
if (isError) { // isError를 사용하여 error가 발생할 때 화면을 랜더링합니다.
return <div>Error: {error.message}</div>;
}
return (
<div>
<h1>User Profile</h1>
<p>Name: {data.name}</p>
<p>Email: {data.email}</p>
</div>
);
}
isLoading, isError 등의 상태값을 확인하여 적절한 화면을 구성한다.
데이터 업데이트, 캐시업데이트를 할수 있으며 POST, PUT, DELETE요청시 사용된다.
const { mutate } = useMutation(
mutationKey, // 없을수도 있음. 있으면 devtool에서 볼수 있음.
mutationFn, // mutation 함수로 promise를 반환한다. fetch, axios등을 사용한다.
options, // 옵션
);
다음은 유저정보를 업데이트 하는 예이다.
import { useMutation, useQueryClient } from 'react-query';
// 데이터 업데이트 함수
function updateUser(userId, updatedData) {
return fetch(`/api/user/${userId}`, {
method: 'PUT',
body: JSON.stringify(updatedData),
}).then((response) => response.json());
}
function UserProfileEditor({ userId }) {
const queryClient = useQueryClient();
const { mutate } = useMutation((updatedData) => updateUser(userId, updatedData), {
onSuccess: () => {
// 데이터 업데이트 후 캐시를 재로드
// user정보가 업데이트 되었으니 유저정보를 다시 로드하도록 한다.
queryClient.invalidateQueries(['user', userId]);
},
});
const handleSubmit = (updatedData) => {
mutate(updatedData); // mutate는 자동으로 실행되지 않기 때문에 submit 시에 mutate 실행
};
return (
<div>
<h2>Edit User Profile</h2>
<UserForm onSubmit={handleSubmit} />
</div>
);
}
UserForm이라는 콤포넌트가 유저정보를 처리하고 있고 onSubmit시 handleSubmit이 호출된다. handleSubmit은 useMutation으로 정의된 mutate함수를 호출한다. 이 때 UserForm에서 작업된 업데이트할 데이터를 인자로 받는다.
useMutation이 정의된 대로 updateUser가 호출되면서 PUT 전송이 발생하고 그 결과 queryClient의 invalidQueries로 캐시를 재로드한다.
캐시를 무효화하여 데이터를 다시 가져올수 있게 한다.
queryClient.invalidateQueries(); 는 모든 캐시된 쿼리를 무효화한다.
queryClient.invalidateQueries({queryKey: ['todos']}) 는 todos로 시작하는 키에 해당하는 쿼리를 모두 무효화한다.
const { data } = useQuery(['data', getServerData,{
staletime: 10 * 60 * 1000, // 10분
cachetime: 30 * 60 * 1000 // 30분
})
이해하기 쉽게 정리하면 30분동안은 서버에 재조회를 하지 않는다. 10분동안은 캐시된 정보로 빠르게 UI를 업데이트한다. cachetime이 staletime보다 반드시 커야 한다.
App 아래 콤포넌트를 추가하면 ReactQuery가 조회를 모니터링하면서 디버깅이 가능하다.
그러면 아래와 같은 화면을 볼 수 있고 조회된 데이터들을 모두 볼수가 있다.
