1. 기본 설정, Devtools, Options
React Query의 설치 및 초기 설정
$ npm i @tanstack/react-query # or $ pnpm add @tanstack/react-query # or $ yarn add @tanstack/react-query
const queryClient = new QueryClient() function App() { return ( // Provide the client to your App <QueryClientProvider client={queryClient}> <Todos /> </QueryClientProvider> ) ========== function Todos() { // Access the client const queryClient = useQueryClient() ...
React Query Devtools의 설치 및 활용 방법
$ npm i @tanstack/react-query-devtools # or $ pnpm add @tanstack/react-query-devtools # or $ yarn add @tanstack/react-query-devtools
기본적으로 React Query Devtools는 번들에만 포함되므로 프로덕션 빌드 중에 제외하는 것에 대해 걱정할 필요가 없습니다.
function App() { return ( <QueryClientProvider client={queryClient}> {/* The rest of your application */} <ReactQueryDevtools initialIsOpen={false} /> </QueryClientProvider> ) }
React Query의 주요 옵션들에 대한 이해
new QueryClient({
defaultOptions: {
queries: {
retry: 0,
useErrorBoundary: true,
},
mutations: {
useErrorBoundary: true,
},
},
});
- React Query의 전반적인 아키텍처와 데이터 흐름에 대한 이해
2. useQuery와 관련된 주요 리턴 데이터, 주요 옵션
- useQuery 기본 문법
- 쿼리키는 업로드에서도 사용이 됨.
- 옵션에서 중요한것 : staleTime(얼마동안 보여줄지), cacheTime(몇분동안 캐싱할지)
polling: 일정한 간격을 두고 계속 fetching요청을 보내줌(거의 실시간. 단점:과부하)
enabled refetching : true
useQuery 주요 리턴 데이터, v4부터의 변화
staleTime과 cacheTime, refetchOnMount, refetchOnWindowFocus, Polling, enabled refetch
3. useQuery 고급 기능 및 useQueryClient
- onSuccess, onError, onSettled
- select, keepPreviousData, placeholderData
- Parallel, Dependent Queries, useQueryClient
- useQuery의 delayQuery 이해 및 활용
4. Infinite Queries 및 초기 쿼리 데이터, Prefetching
React Query는 무한 스크롤의 손쉬운 구현을 위해 useInfiniteQuery
Hook을 지원합니다.
- 페이지 정보에 대한 관리
- 로딩 상태 관리
- 데이터 관리
- 다음 페이지 호출 진행 여부에 대한 관리
위 번거로운 과정들을 각각의 상태로 자동으로 만들어주기 때문에 UI 구현에만 집중할 수 있습니다.
- Infinite Queries 사용 방법
- 참고 : 단순히 다음 페이지를 불러오는 방식만 고려한 것이 아니라 양방향 스크롤도 고려하여 만들어졌기에 유연하게 사용하실 수 있습니다.
import { useInfiniteQuery } from 'react-query';
import axios from 'axios';
function Projects() {
const fetchProjects = async ({ pageParam = 1 }) => {
const response = await axios.get(`/api/projects?page=${pageParam}`);
return response.data;
};
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
status,
} = useInfiniteQuery('projects', fetchProjects, {
getNextPageParam: (lastPage, allPages) => lastPage.nextPage,
});
if (status === 'loading') {
return <div>Loading...</div>;
}
if (status === 'error') {
return <div>Error: {error.message}</div>;
}
return (
<div>
{data.pages.map((group, i) => (
<React.Fragment key={i}>
{group.projects.map((project) => (
<div key={project.id}>{project.name}</div>
))}
</React.Fragment>
))}
<div>
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage
? 'Loading more...'
: hasNextPage
? 'Load More'
: 'Nothing more to load'}
</button>
</div>
</div>
);
}
export default Projects;
const {
data, // 데이터
error, // 에러
fetchNextPage, // 다음 페이지 데이터를 불러올 수 있는 함수
fetchPreviousPage, // 이전 페이지 데이터를 불러올 수 있는 함수
hasNextPage, // 다음 페이지가 존재하는지 여부를 boolean으로 반환
hasPreviousPage, // 이전 페이지가 존재하는지 여부를 boolean으로 반환
isFetchingNextPage, // 다음 페이지를 불러오고 있는 중인지 여부를 boolean으로 반환
isFetchingPreviousPage, // 이전 데이터를 불러오고 있는 중인지 여부를 boolean으로 반환
status, //
} = useInfiniteQuery({
queryKey: ['page'],
queryFn: ({ pageParam = 1 }) => fetchPage(pageParam),
getNextPageParam: (lastPage, pages) => (lastPage, allPages) => lastPage.data.id + 1,
getPreviousPageParam: (firstPage, allPages) => firstPage.data.id + 1,
})
})
lastPage는 useInfiniteQuery를 이용해 호출된 가장 마지막에 있는 페이지 데이터를 의미합니다.
allPages는 useInfiniteQuery를 이용해 호출된 모든 페이지 데이터를 의미합니다.
getNestPageParam에서 return 되는 값이 다음 페이지가 호출될 때 pageParam 값으로 사용됩니다.
그렇기 때문에 위 예시에서는 lastPage에 있는 데이터의 id값보다 1 더 큰 값을 return값으로 하여 다음 페이지가 호출될 수 있도록 해놨습니다.
Initial Query Data 설정 방법
function Todos() { const result = useQuery(['todos'], () => fetch('/todos'), { initialData: initialTodos, }) }
function Todos() { const result = useQuery(['todos'], () => fetch('/todos'), { initialData: initialTodos, staleTime: 60 * 1000, // 1 minute initialDataUpdatedAt: initialTodosUpdatedTimestamp // eg. 1608412420052 }) } //다른 쿼리의 캐시된 결과를 이용하여 쿼리의 초기 데이터를 설정하는 것도 가능합니다. // 예를 들어, 리스트에서 개별 아이템의 쿼리에 사용할 초기 데이터를 설정하는 경우에 //사용할 수 있습니다.
function Todos() { const result = useQuery(['todos'], () => fetch('/todos'), { initialData: () => { return getExpensiveTodos() }, }) } //initialData에 함수를 전달하는 방법도 있습니다. //이 함수는 쿼리가 초기화될 때 한 번만 실행되며, //계산 비용이 큰 작업이 필요한 경우에 유용하게 사용할 수 있습니다.
function Todo({ todoId }) { const result = useQuery(['todo', todoId], () => fetch('/todos'), { initialData: () => { return queryClient.getQueryData(['todos'])?.find(d => d.id === todoId) }, }) } //다른 쿼리의 캐시된 결과를 이용하여 쿼리의 초기 데이터를 설정하는 것도 가능합니다. //예를 들어, 리스트에서 개별 아이템의 쿼리에 사용할 초기 데이터를 설정하는 경우에 //사용할 수 있습니다.
function Todo({ todoId }) { const result = useQuery(['todo', todoId], () => fetch(`/todos/${todoId}`), { initialData: () => queryClient.getQueryData(['todos'])?.find(d => d.id === todoId), initialDataUpdatedAt: () => queryClient.getQueryState(['todos'])?.dataUpdatedAt, }) } //원본 쿼리가 오래된 경우에는 캐시된 데이터를 사용하지 않고 서버에서 데이터를 가져오는 것이 //좋을 수 있습니다. 이런 결정을 쉽게 하기 위해 queryClient.getQueryState 메소드를 //사용하여 쿼리가 얼마나 fresh한지 결정할 수 있습니다. --- function Todo({ todoId }) { const result = useQuery(['todos', todoId], () => fetch(`/todos/${todoId}`), { initialData: () => { const state = queryClient.getQueryState(['todos']) if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) { return state.data.find(d => d.id === todoId) } }, }) }
TanStack Query에는 초기데이터를 활용하는 두가지 방법이 있다.
Prefetching의 이해 및 활용
5. useMutation과 관련된 주요 기능 및 v5변경점
- useMutation 기본 사용법
- mutate와 mutateAsync 비교
- cancelQueries 이해 및 활용
- invalidateQueries 이해 및 활용
6. 쿼리 무효화 및 캐시 데이터 업데이트, Optimistic Update
- Lazy Queries
- 쿼리 무효화 방법 및 활용
- 캐시 데이터 즉시 업데이트 방법
- Optimistic Update 이해 및 활용
7. 기타 특징 및 고급 기능 -
시간남는분만 하시길 권장
- useQueryErrorResetBoundary 이해 및 활용
- React Query와 Suspense의 통합 이해 및 활용
- Default Query Function 이해 및 활용
- React Query와 TypeScript의 통합 이해 및 활용