이번 포스팅에서는 Tanstack Query의 useQueries사용법에 대해 적어보는 시간을 가져보려고 합니다.
들어가기 앞서 useQuery의 사용법을 알고 있다는 가정하에 포스팅을 진행합니다. useQuery의가 궁금하신 분들은 해당 포스팅을 먼저 참고해주세요.
import React from 'react';
import { useQuery } from '@tanstack/react-query';
const fetchUser = async (userId) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
return response.json();
};
const UserProfile = ({ userId }) => {
const { data: user, isLoading, error } = useQuery(['user', userId], () => fetchUser(userId));
return (
<div>
<h2>User Profile</h2>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
};
const App = () => {
return (
<>
<UserProfile userId={1} />
</>
);
};
export default App;
위 코드는 useQuery를 사용하여 userId를 api 요청시 전달하여 해당 id에 관한 유저 정보를 불러오는 간단한 예시 코드입니다. 해당 코드는 문제 없이 잘 작동합니다.그렇지만 여기서 userId가 하나가 아닌 여러 개의 userId 값이 존재하는 배열이 전달된 경우 어떻게 useQuery를 수행해야 할까요?
useQuery로도 병렬적인 다건의 API 요청을 수행할 수 있지만, 서로 다른 userId를 100개이상 화면에 보여줘야 한다고 가정하면 코드 길이가 무수히 늘어나고, API 요청이 동적으로 변경되는 것이 아니라 정적인 상태로 한정될 것입니다. 이때, 위 상황을 방지할 수 있는것이 useQueries
입니다.
useQueries는 Tanstack Query에서 useQuery의 동적 병렬 쿼리 작업을 위해 사용됩니다.
그리고 여기서 말하는 동적 병렬 쿼리 작업은 병렬 쿼리 작업을 수행을 하지만 상황에 따라 쿼리 작업이 유동적으로 변하는 것을 의미합니다.
import React from 'react';
import { useQuery, useQueries } from '@tanstack/react-query';
const fetchUser = async (userId) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
return response.json();
};
const UsersProfile = ({ userIds }) => {
// 여러 사용자의 데이터를 병렬로 가져오기 위해 useQueries를 사용
const userQueries = useQueries(
userIds.map((userId) => ({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
}))
);
return (
<div>
<h2>Users Profile</h2>
{userQueries.map((query, index) => {
const user = query.data;
return (
<div key={index}>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
})}
</div>
);
};
const App = () => {
return (
<div>
<UsersProfile userIds={[1, 2, 3]} />
</div>
);
};
export default App;
uUsersProfile 컴포넌트가 props로 받은 userIds 배열을 map 함수를 통해 각각의 id를 queryKey와 queryFn 항목만 지정해서 리턴하면 useQueries 함수가 알아서 동적 병렬 쿼리를 생성하여 보다 효율적으로 구현할 수 있게 도와줍니다.
추가로 프로젝트에 useQueries를 어떻게 적용했는지 보여드리겠습니다.
import { useQueries } from '@tanstack/react-query';
import { useRecoilValue } from 'recoil';
import styled from 'styled-components';
import { myLangAtom, myMovieAtom, myTvAtom } from '../atom';
import { getDetails } from '../utils/api';
import Loader from '../components/Loader';
import MyListGrid from '../components/MyListGrid';
function MyList() {
const { t } = useTranslation();
const lang = useRecoilValue(myLangAtom);
const myMovie = useRecoilValue(myMovieAtom);
const myTv = useRecoilValue(myTvAtom);
const myMovieQuery = useQueries({
queries: myMovie.map((movieId) => {
return {
queryKey: ['myMovie', String(movieId), lang],
queryFn: () => getDetails('movie', String(movieId), lang),
};
}),
});
const myTvQuery = useQueries({
queries: myTv.map((tvId) => {
return {
queryKey: ['myTv', String(tvId), lang],
queryFn: () => getDetails('tv', String(tvId), lang),
};
}),
});
const myMovieData = myMovieQuery?.map((myMovie) => myMovie.data);
const myTvData = myTvQuery?.map((myTv) => myTv.data);
const isMyMovieLoading = myMovieQuery.some((myMovie) => myMovie.isLoading);
const isMyTvLoading = myTvQuery.some((myTv) => myTv.isLoading);
const isLoading = isMyMovieLoading || isMyTvLoading;
if (isLoading) {
return <Loader />;
}
return (
<>
<Wrapper>
<MyListGrid
title={t('mylist.movie')}
contents={myMovieData}
section='movie'
altText={t('mylist.altText')}
/>
<MyListGrid
title={t('mylist.tv')}
contents={myTvData}
section='tv'
altText={t('mylist.altText')}
/>
</Wrapper>
</>
);
}
export default MyList;
유저가 보관함에 추가한 각 영화, TV의 작품들의 id를 배열 형태로 받아온 뒤, useQueries를 사용하여 각 영화 및 TV 프로그램에 대한 쿼리를 생성합니다.
각 쿼리는 getDetails 함수를 호출하여 해당 작품의 세부 정보를 가져옵니다. 이때 쿼리 키에는 해당 작품의 ID와 언어가 포함된 각각의 쿼리를 동적으로 생성해줍니다. 각 쿼리의 로딩 상태를 확인하여 전체 데이터가 로드되었는지를 판단하여 로딩 중에는 각각의 로딩 컴포넌트를 보여주고, 데이터가 로드된 후에는 실제 내용을 표시합니다.
Reference: