Dependant Query : useQuery()의 enabled 옵션 사용
만약 쿼리들이 순서대로 실행되어야 할 경우, useQuery()의 enabled 옵션을 사용할 수 있다.
이 값이 true일 때만 쿼리를 실행시키는 옵션이다.
만약
const {
data: projects,
} = useQuery({
queryKey: ['projects', userId],
queryFn: getProjectsByUser,
enabled: !!userId,
});
이렇게 enable 옵션을 설정한다면,
userId가 있다면 true, 없다면 false가 되어 userID의 존재유무에 따라 실행유무를 달리할 수 있다.
예제로 확인해봅시다.
Q. currentUsername 값이 있다면, 해당 아이디 값을 이용해 유저의 이름, 프로필 사진 등의 정보를 받아오는 쿼리를 작성하려고 합니다.
const { data: currentUserInfo } = useQuery({
queryKey : ['userInfo', usernames],
queryFn : () => getUserInfo(currentUsername), // 유저정보를 불러옴
enabled : !!currentUsername, // 유저 값이 있을 때만 해당 유저의 데이터를 가져오도록 함
staleTime: 1000 * 60 * 60, // 매번 refetch 하지않도록 60분 설정
});
위와 같이 코드를 작성해볼 수 있다.
그럼 로그인 전에는 userInfo가 disabled 상태이다가, 로그인을 하게 되면 해당 유저 아이디로 데이터를 잘 캐싱하게된다.
const handleLogoutClick = () => {
queryClient.removeQueries({
queryKey: ['userInfo', currentUsername],
});
setCurrentUsername(undefined);
navigate('/');
};
만약 로그아웃을 한다면 queryClient.removeQueries를 활용하여
currentUsername을 undefined로 바꿔주고, 기존 userInfo 값을 삭제시키면 된다.
Paginated Query : 페이지네이션 구현
데이터(게시글)를 한번에 다! 말고 끊어서 불러오고 싶다!
const PAGE_LIMIT = 3;
function HomePage() {
// ...
const [page, setPage] = useState(0);
const {
data: postsData,
isPending,
isError,
} = useQuery({
queryKey: ['posts', page],
queryFn: () => getPosts(page, PAGE_LIMIT),
});
이렇게 page를 설정해두고, getPosts 함수에서도 page에서 limit만큼 불러오게 한다면?
-> 첫 페이지(0)에 해당하는 3개 값만 보인다.
이 posts의 데이터를 살펴보면, hasMore이라는 옵션이 있다. 다음 페이지가 있을 때 이걸 true로 보내준다.
<button
disabled={!postsData?.hasMore}
onClick={() => setPage((old) => old + 1)}
>
따라서 hasMore값에 따라서 다음페이지로 가는 버튼을 비활성화 할 지 말지 결정할 수 있다!
페이지네이션에서 부드러운 UI 전환
placeholderData라는 옵션이 존재한다. 이 옵션의 값을 keepPreviousData로 설정하면,
페이지가 바뀔 때 새로 pending 상태가 되는 것이 아니라, 이전의 데이터를 보여주다가 fetch가 끝나면 새 데이터로 바꿔 보여주게 된다.
<button
disabled={isPlaceholderData || !postsData?.hasMore}
onClick={() => setPage((old) => old + 1)}
>
에러를 방지하기 위해 중간에 업데이트 되는동안은 다음 페이지 버튼을 비활성화 할 필요도 있다.
여기서 이제 좀 더 깔끼하게 페이지 로딩을 구현하려면, prefetch하는 방법이 있다.
useEffect(() => {
if (!isPlaceholderData && postsData?.hasMore) {
queryClient.prefetchQuery({
queryKey: ['posts', page + 1],
queryFn: () => getPosts(page + 1, PAGE_LIMIT),
});
}
}, [isPlaceholderData, postsData, queryClient, page]);
뭐 이런식으로 미리 다음페이지 데이터를 fetch해 놓을 수 있기 대문에 좋다.
근데 내 체감상 시중의 서비스들을 보면, 로딩이 1초이상 걸리는데 로딩중이라는 문구가 뜨지 않는 경우가 많다.
아마 이런식으로 이전의 데이터를 계속 보여주게 설정해서겠지.
근데 그게 썩 좋진 않다. 괜히 렉 걸린 것 같은 느낌이 많이 들어서, 1초 이상(?) 뭐 어느정도 데이터가 많아 처리 시간이 좀 걸린다면 차라리 로딩중이라고 뜨는것도 나쁘지 않을 것 같다.
infinite Query : 페이지네이션 대신, 더 불러오기를 구현한다면!
먼저 기존의 useQuery()를 useInfiniteQuery()로 바꿔주어야한다.
이는 initialPageParam과 getNextPageParam이라는 옵션을 필수로 가진다.
const {
data: postsData,
isPending,
isError,
fetchNextPage,
} = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam }) => getPosts(pageParam, PAGE_LIMIT),
initialPageParam: 0,
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.hasMore ? lastPageParam + 1 : undefined,
});
useQuery()에서의 data는 서버에서 한 페이지만의 정보를 받아와 담고있다면,
useInfiniteQuery()에서는 data.pages에 배열 형태로 모든 페이지 정보를 담고 있다.
첫 페이지의 데이터를 받아오면 data.pages 배열의 0번째 인덱스에 그 데이터가 저장되는 것이다.
이렇게 쌓이는 데이터들이 ['posts']라는 쿼리 키로 캐싱된다.
initialPageParam 옵션은 초기 페이지 설정 값을 API에 맞게 설정해야 하고,
getNextpageParam 옵션은 다음 페이지의 설정 값을 정하게 된다.
파라미터 중
lastPage : 현재까지 가장 마지막 페이지의 데이터, allPages : 모든 페이지의 데이터,
lastPageParam : 가장 마지막 페이지의 설정값, allPageParams : 모든 페이지의 설정값이다.
위 파라미터 값들을 통해 다음 페이지 값인 pageParam을 리턴한다.
따라서 hasMore 값을 통해 다음 페이지가 존재한다면, lastPageParam의 값+1을 리턴하도록 한다.
이 값(pageParam)은 queryFn의 파라미터로 전달되어 백에 데이터를 요청하게 되는 것이다!
이제 다음 값을 불러오려면, useInfiniteQuery()의 리턴값 중 하나인 fetchNextPage()를 이용하면 된다!
위의 pageParam값이 null, undef가 아니라면 그 값을 쿼리함수의 pageParam으로 전달하여 데이터를 가져온다.
-> onclick()의 함수로 등록해주면 되는 것!!!
코드잇의 강의를 통해 학습한 내용을 작성하였습니다~!@!@!
'Upscaling' 카테고리의 다른 글
13. 리액트에서 사용하는 타입스크립트 (0) | 2024.04.17 |
---|---|
12. TS(타입스크립트) 기본 문법 및 타입 정리 (0) | 2024.04.17 |
10. 리액트 쿼리 기초 (0) | 2024.03.18 |
8. 스박사님을 아세욧? (0) | 2024.03.10 |
6. append와 extend (1) | 2024.03.06 |