가수면
[React Query] useQuery 본문
설치
npm i @tanstack/react-query
npm i @tanstack/react-query-devtools
QueryClient와 QueryClientProvider 추가 (ReactQueryDevtools은 선택 사항)
import { Posts } from "./Posts";
import "./App.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
// 추가
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<div className="App">
<h1>Blog Posts</h1>
<Posts />
</div>
<ReactQueryDevtools />
</QueryClientProvider>
);
}
export default App;
useQuery
import { useQuery } from "@tanstack/react-query";
.
.
.
async function fetchPosts() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=10&_page=0");
return response.json();
}
// useSelector 같은 기능
// 인자, 사용할 함수 이름, stale이라고 판단할 시간 설정(밀리초 단위))
const { data, isError, isLoading, error } = useQuery(["posts"], fetchPosts, { staleTime: 2000 });
// 비동기 처리에 대한 대처
//if (!data) return <div />; 세련된 방법은 아님
if (isLoading) return <h3>로딩 중</h3>;
if (isError)
return (
<>
<h3>알 수 없는 오류</h3>
<p>{error.toString()}</p> // 세련된 방법은 아님
</>
);
.
.
.
map 돌림
디테일 페이지 데이터 불러올 때 / 쿼리 함수에 값을 실어야할 경우
// 의존성 배열 + 객체 느낌. post.id가 업데이트되면 새 쿼리를 생성, 데이터가 바뀌면 키도 바뀌도록
const { data, isLoading, isError } = useQuery(["comments", post.id], () => fetchComments(post.id));
useQuery옵션
select
원래 return되었을 데이터를 가져와서 변환한 다음 변환한 데이터를 return해줌
const selectFn = useCallback(
(data) => {
getAvailableAppointments(data, user);
},
[user]
);
const { data: appointments = [] } = useQuery(
[queryKeys.appointments, monthYear.year, monthYear.month],
() => getAppointments(monthYear.year, monthYear.month),
{
select: showAll ? undefined : selectFn,
}
);
refetch 옵션 끄기
정적인 페이지에서는 비용 / 성능 측면에서 모두 네트워크와 통신할 필요가 없음
export function useTreatments() {
const { data = [] } = useQuery(queryKeys.treatments, getTreatments, {
staleTime: 600000,
cacheTime: 900000,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
return data;
}
refetchInterval
refetchInterval: 60000,
enabled
export function useUserAppointments(): Appointment[] {
const { user } = useUser();
const { data: useAppointments = [] } = useQuery(
'user=appointments',
() => getUserAppointments(user),
{ enabled: !!user },
);
return useAppointments;
}
로그인 했을 때 키를 쥐어주고 그 화면 불러온 뒤
function clearUser() {
queryClient.setQueryData(queryKeys.user, null);
queryClient.removeQueries('user-appointments');
}
불필요할 땐 키를 제거해 렌더링을 안불러오게 함
Uncaught TypeError: Cannot read properties of undefined (reading 'map') 에러가 뜰 경우
// 빈 배열을 할당해줌
const { data = [] } = useQuery(queryKeys.treatments, getTreatments);
데이터를 다시 가져오게되는 트리거 (리페칭)
- 컴포넌트 재 마운트
- 윈도우 재 포커스
- useQuery에서 반환되어 수동으로 리페칭할 때
- 지정된 간격으로 리페칭이 자동 실행될 때
- Mutation을 생성한 뒤 쿼리를 무효화할 때
- 클라이언트의 데이터가 서버의 데이터와 불일치할 때
페이지 네이션
async function fetchPosts(pageNum) {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts?_limit=10&_page=${pageNum}`);
return response.json();
}
const { data, isError, isLoading, error } = useQuery(["posts", currentPage], () => fetchPosts(currentPage), {
staleTime: 2000,
//쿼리 키가 바뀌어도 이전 데이터 유지
keepPreviousData: true,
});
.
.
.
<div className="pages">
<button disabled={currentPage <= 1} onClick={() => setCurrentPage(currentPage - 1)}>
Previous page
</button>
<span>Page {currentPage}</span>
<button disabled={currentPage >= maxPostPage} onClick={() => setCurrentPage(currentPage + 1)}>
Next page
</button>
</div>
prefetchQuery
데이터를 미리 캐싱할 수 있게 해주는 메소드
const queryClient = useQueryClient();
useEffect(() => {
if (currentPage < maxPostPage) {
const nextpage = currentPage + 1;
queryClient.prefetchQuery(["posts", nextpage], () => fetchPosts(nextpage));
}
}, [currentPage, queryClient]);
커스텀 훅으로 만들 시 useEffect 내에서 훅을 실행할 수 없음
staleTime이나 cachTime 등의 옵션으로 조절
export const usePrefetchTreatments = (): void => {
const queryClient = useQueryClient();
queryClient.prefetchQuery(queryKeys.treatments, getTreatments);
};
-Home.tsx-
export function Home(): ReactElement {
usePrefetchTreatments();
return (
에러 핸들링
queryClient에 디폴트 값으로 줌으로써 전역적인 에러 핸들링을 할 수 있다.
import { QueryClient } from 'react-query';
function queryErrorHandler(error: unknown): void {
const title =
error instanceof Error ? error.message : 'error connecting to server';
return 에러처리하고 싶은 방식
}
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
onError: queryErrorHandler,
},
},
});
onError
const {data = [], isError, isLoading, error} = useQuery(["main"], mainApi.read, {
onError: (error) => {
const { status } = error?.response.request;
if (status === 401) {
dispatch(showModal({ isModal: true, content: "로그인 후 이용해주세요.", move: "/login" }));
} else if (status === 400)
return dispatch(showModal({ isModal: true, content: "일기장 조회에 실패했습니다.", move: "/login" }));
},
});
위와 같이 코드를 짜면 에러 발생 시 바로 모달창이 뜨지 않고 리패칭해야만 모달창이 뜨게 된다.
해결법은 리렌더링을 일으키는 것.
// isError일 때 태그를 생성함으로써 리렌더링을 일으킴
return (
<>
{isLoading ? (
<h2>로딩 중...</h2>
) : isError ? (
<h2>{`${error?.response.status} ERROR`}</h2>
) : (~~~
'React > 라이브러리' 카테고리의 다른 글
[React Query] useMutation (0) | 2023.01.06 |
---|---|
[React Query] isFetching vs isLoding (0) | 2023.01.06 |
리액트 훅 폼 (useForm) (0) | 2023.01.03 |
[Styled Components] Styled Components 심화 (0) | 2022.12.19 |
[Styled Components] Styled Components Global Style (0) | 2022.12.17 |
Comments