77일차 TIL : RN 영화 리뷰앱 - React Query

변시윤·2023년 1월 15일
0

내일배움캠프 4기

목록 보기
81/131
post-custom-banner

학습내용

  • React-Query
  • useQuery
  • queryClient
  • useInfiniteQuery

React-Query란?

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>
   )
 }



useQuery

Get or Fetch 요청시 사용하는 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를 사용한다.



queryClient

모든 컴포넌트의 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를 생성하지 않아도 된다.



useInfiniteQuery

React-Query의 무한스크롤 hook. queryKey, queryFn을 인자로 받는 useQuery의 사용법과 동일하나 세 번째 인자를 추가로 갖는다. 또한 QueryFnpageParam이라는 프로퍼티를 매개변수로 넘겨준다.

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()}
     />
   };
  1. 마운트시 fetch 함수 실행
  2. 1에 대한 결과로 pageParams, page의 결과값을 포함한 객체를 반환

FlatList의 속성

  • onEndReached
    스크롤이 리스트의 마지막 부분에 도달시 추가로 fetch 하는 트리거
  • onEndReachedThreshold
    추가 fetchfetch할 양을 결정하는 속성
    1 = 디바이스의 스크린 높이

array.flat()
중첩된 배열을 평탄화하는 배열 메서드

profile
개그우먼(개발을 그은성으로 하는 우먼)
post-custom-banner

0개의 댓글