tanstack query v5 Performance & Request Waterfalls
Request Waterfalls (요청폭포)가 발생할 수 있는 몇가지 예시를 설명하며, 해결방법을 제시합니다.
요청 폭포는 리소스(CODE, CSS, Image, DATA)에 대한 요청이 리소스에 대한 다른 요청이 완료될 때 까지 시작되지 않을 때 발생합니다.
두 번째 쿼리에 enabled 옵션 설정으로 종속쿼리로 정의되어 있습니다.
첫 번째 쿼리가 실행 완료 후 두 번째 쿼리가 실행됩니다.
-> 요청폭포 발생
import { useQuery } from '@tanstack/react-query';
import { axiosClient } from '../../api/apiClient';
const Dependant = () => {
const { data: todoList, isFetched } = useQuery({
queryKey: ['todo'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/todos'),
});
const { data: postList } = useQuery({
queryKey: ['posts'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/posts'),
enabled: isFetched,
});
return (
<div style={{ display: 'flex' }}>
<section>
<h2>Todo List</h2>
{todoList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
</section>
<section>
<h2>Post List</h2>
{postList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
</section>
</div>
);
};
export default Dependant;
network waterfall이 생성되고있습니다. 따라서 서비스의 Loading이 길어지는 현상이 발생합니다.
Suspense와 함께 React 쿼리를 사용하는 경우 발생합니다.
// SuspenseQuery.ts
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { axiosClient } from '../../api/apiClient';
const SuspenseQuery = () => {
const { data: todoList } = useSuspenseQuery({
queryKey: ['todo'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/todos'),
});
const { data: postList } = useSuspenseQuery({
queryKey: ['posts'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/posts'),
});
const { data: photoList } = useSuspenseQuery({
queryKey: ['photo'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/photos'),
});
const { data: userList } = useSuspenseQuery({
queryKey: ['users'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
});
return (
<div style={{ display: 'flex' }}>
<section>
<h2>Todo List</h2>
{todoList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
</section>
<section>
<h2>Post List</h2>
{postList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
</section>
<section>
<h2>photo List</h2>
{photoList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
</section>
<section>
<h2>user List</h2>
{userList?.data.map(({ id, title }: { id: number; title: string }) => <div key={id}>{title}</div>)}
</section>
</div>
);
};
export default SuspenseQuery;
// QueryTest.tsx
import { Suspense } from 'react';
import SuspenseQuery from './SuspenseQuery';
const QueryTest = () => {
return (
<Suspense fallback={<div>...loading</div>}>
<SuspenseQuery />
</Suspense>
);
};
export default QueryTest;
useSuspneseQueries
를 사용하면 간단하게 해결 할 수 있습니다.
// QueryTest.tsx
const [todoList, postList, photoList, userList] = useSuspenseQueries({
queries: [
{ queryKey: ['todo'], queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/todos') },
{
queryKey: ['posts'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/posts'),
},
{
queryKey: ['photo'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/photos'),
},
{
queryKey: ['users'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
},
],
});
network waterfall 해결되었습니다.
중첩 구성 요소 폭포는 상위 구성 요소와 하위 구성 요소 모두에 쿼리가 포함되어 있고
상위 구성 요소는 쿼리가 완료될 떄까지 하위 구성 요소를 렌더링하지 않는 경우입니다.
useQuery
, useSuspenseQuery
모두 발생할 수 있습니다.
// User.tsx
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { axiosClient } from '../../api/apiClient';
import Comments from './Comments';
const Users = () => {
const { data: userList, isPending } = useQuery({
queryKey: ['users'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
});
if (isPending) return <div>Loading....</div>;
return (
<div>
<ul>
{userList?.data.map((user: any) => {
return <li key={user.id}>{user.name}</li>;
})}
</ul>
<Comments postId={1}></Comments>
</div>
);
};
export default Users;
// Comments.tsx
import { useQuery } from '@tanstack/react-query';
import { axiosClient } from '../../api/apiClient';
const Comments = ({ postId }: { postId: number }) => {
const { data: comments, isPending } = useQuery({
queryKey: ['comments'],
queryFn: () => axiosClient.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`),
});
if (isPending) return <div>Loading....</div>;
return <div>{comments?.data.id}</div>;
};
export default Comments;
상위 항목에서 <User>
컴포넌트를 가져오는 동안 해당 ID는 렌더링 시 이미 사용 가능하므로 동시에 기사와 댓글을 가져올 수 없는 이유가 없습니다.
평탄화를 위해 상위 항목으로 끌어올려 해결 할 수 있습니다.
import { useQuery } from '@tanstack/react-query';
import { axiosClient } from '../../api/apiClient';
import Comments from './Comments';
import Users from './Users';
interface AnswerProps {
postId?: number;
}
const Answer = ({ postId = 1 }: AnswerProps) => {
const { data: commentsData, isPending: commentsPending } = useQuery({
queryKey: ['comments'],
queryFn: () => axiosClient.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`),
});
const { data: usersData, isPending: usersPending } = useQuery({
queryKey: ['users'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
});
return (
<>
<Users usersData={usersData} />
{commentsPending ? 'Loading comments...' : <Comments commentsData={commentsData} />}
</>
);
};
export default Answer;
useQueries
를 사용하여 두 쿼리를 하나로 결합하는 방식이 더 좋습니다.
import { useQueries, useQuery, useSuspenseQueries } from '@tanstack/react-query';
import { axiosClient } from '../../api/apiClient';
import Comments from './Comments';
import Users from './Users';
interface AnswerProps {
postId?: number;
}
const Answer = ({ postId = 1 }: AnswerProps) => {
const queryResults = useQueries({
queries: [
{
queryKey: ['users'],
queryFn: () => axiosClient.get('https://jsonplaceholder.typicode.com/users'),
},
{
queryKey: ['comments'],
queryFn: () => axiosClient.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`),
},
],
});
return (
<>
<Users usersData={queryResults[0].data} />
<Comments commentsData={queryResults[1].data} />
</>
);
};
export default Answer;
It's great to be exposed to these interesting programs. Knowledge geometry dash lite is always diverse and expanding. Easy to absorb a lot. Functions with constantly expanding and improving knowledge.