학습내용
- React-Query
- useQuery
- queryClient
- useInfiniteQuery
Server State(
isLoading
,error
,data
등)에 따른 비동기 데이터를 관리하기 위한 라이브러리. hook 안에서 API 요청과 상태 및 요쳥 결과들을 바로 사용할 수 있기 때문에 redux thunk처럼 상태(pending
,fulfilled
,rejected
)에 따른 결과를 따로 적어줄 필요가 없다. 또한 API 요청시, queryKey가 요청 결과를 cache에 저장하기 때문에 캐싱된 API 요청은 서버에 다시 요청하지 않고 cache memory에서 불러온다.
세팅하기
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Component />
</QueryClientProvider>
)
}
Get
orFetch
요청시 사용하는 hook
api.js
export const getNowPlayings = () =>
fetch(`${BASE_URL}/now_playing?api_key=${API_KEY}&language=ko&page=1`).then(
(res) => res.json()
);
기존의 API 요청 코드는 api.js 파일 생성 후 해당 파일에서 관리
Movies.jsx
const Movies = ({ navigation: { navigate } }) => {
const {
data: nowPlayingData,
isLoading: isLoadingNP,
} = useQuery(["Movies", "NowPlaying"], getNowPlayings);
.
.
.
const isLoading = isLoadingNP || isLoadingRK || isLoadingUC;
if (isLoading) {
return (
<Loader>
<ActivityIndicator />
</Loader>
);
}
return (
<FlatList
refreshing={isRefreshing}
onRefresh={onRefresh}
ListHeaderComponent={
<>
<Container>
<Swiper height="100%" showsPagination={false} autoplay loop>
{nowPlayingData.results
.filter((movie) => movie.overview)
.map((movie) => (
<Slide key={movie.id} movie={movie} />
))}
</Swiper>
</Container>
.
.
.
</>
}
);
};
useState
로 관리하던 상태를 useQuery
로 교체했으므로 데이터 역시 nowPlayings
가 아닌 nowPlayingData.results
를 사용한다.
모든 컴포넌트의 cache에 접근 가능한 관리자(최상위 컴포넌트에서 감싸줬기 때문)
const {
data: nowPlayingData,
isLoading: isLoadingNP,
refetch: refetchNP,
} = useQuery("NowPlaying", getNowPlayings);
const {
data: rankingData,
isLoading: isLoadingRK,
refetch: refetchRK,
} = useQuery("Ranking", getRanking);
const {
data: upcomingData,
isLoading: isLoadingUC,
refetch: refetchUC,
} = useQuery("Upcoming", getUpcoming);
const onRefresh = async () => {
setIsRefreshing(true);
await Promise.all([refetchNP(), refetchRK(), refetchUC()]);
};
refetch 작업을 위해 각각의 useQuery
마다 refetch
에 대한 state를 추가 후 Promise.all()
로 처리했다. queryClient를 사용하면 이를 더 간단한 방법으로 처리할 수 있다.
const queryClient = useQueryClient();
const {
data: nowPlayingData,
isLoading: isLoadingNP,
} = useQuery(["Movies", "NowPlaying"], getNowPlayings);
const {
data: rankingData,
isLoading: isLoadingRK,
} = useQuery(["Movies", "Ranking"], getRanking);
const {
data: upcomingData,
isLoading: isLoadingUC,
} = useQuery(["Movies", "Upcoming"], getUpcoming);
const onRefresh = async () => {
setIsRefreshing(true);
await queryClient.refetchQueries(["Movies"]);
setIsRefreshing(false);
};
queryKey로 cache memory에 접근이 가능한 React-Query의 특성을 이용해 "Movies"
라는 공통의 QueryKey를 생성해 관리하는 방법이다. 해당 방법을 사용하면 각각의 useQuery
마다 refetch
state를 생성하지 않아도 된다.
React-Query의 무한스크롤 hook.
queryKey
,queryFn
을 인자로 받는useQuery
의 사용법과 동일하나 세 번째 인자를 추가로 갖는다. 또한QueryFn
에pageParam
이라는 프로퍼티를 매개변수로 넘겨준다.
api.js
export const getUpcoming = ({ pageParam = 1 }) => {
return fetch(
`${BASE_URL}/upcoming?api_key=${API_KEY}&language=ko&page=${pageParam}`
).then((res) => res.json());
};
Movies.jsx
const {
data: upcomingData,
isLoading: isLoadingUC,
fetchNextPage,
hasNextPage,
} = useInfiniteQuery(["Movies", "Upcoming"], getUpcoming, {
getNextPageParam: (lastPage) => {
if (lastPage.page < lastPage.total_pages) {
return lastPage.page + 1;
}
},
});
const fetchMore = async () => {
if (hasNextPage) {
await fetchNextPage();
}
};
.
.
.
return (
<FlatList
refreshing={isRefreshing}
onRefresh={onRefresh}
onEndReached={fetchMore}
onEndReachedThreshold={0.5}
ListHeaderComponent={
.
.
.
}
data={upcomingData.pages.map((page) => page.results).flat()}
/>
};
- onEndReached
스크롤이 리스트의 마지막 부분에 도달시 추가로fetch
하는 트리거
- onEndReachedThreshold
추가fetch
시fetch
할 양을 결정하는 속성
1 = 디바이스의 스크린 높이
array.flat()
중첩된 배열을 평탄화하는 배열 메서드