qcoding

[ReactNative_Study_11]무한스크롤 구현 (Flat List & React query) 본문

ReactNative

[ReactNative_Study_11]무한스크롤 구현 (Flat List & React query)

Qcoding 2021. 12. 6. 16:10
반응형

FlastList & React query 사용하여 무한 스크롤을 구현한다.

1) Flat List

  --> FlastList props 중에서 onEndReached , props를 사용한다.

    <FlatList
    	// 무한스크롤 구현시 사용하는 props
        // Threshold에 도달했을 때 실행되는 함수가 onEndReached가 된다.
        onEndReached={}
        onEndReachedThreshold={}
        
        onRefresh={}
        refreshing={}
        ListHeaderComponent={}
        data={}
        keyExtractor={}
        ItemSeparatorComponent={}
        renderItem={}
      />

2) React Query

--> react query에서 infinitie-queries를 사용한다.

 

2-1 ) Fetch 시에 기존에 data 받아오는 구조 변경하기 

https://react-query.tanstack.com/guides/infinite-queries

 

Infinite Queries

Subscribe to our newsletter The latest TanStack news, articles, and resources, sent to your inbox.

react-query.tanstack.com

--> 기존에 data를 fetch할 때 useQuery() 를 사용하였는데 여기서 fetch후 받아오는 trendingData의 경우 {} Object의 형태를 갖는 것이 useInfiniteQuery ()와는 다르다.

   const {isLoading:trendingLoading,data:trendingData,
          isRefetching:trendingRefetching}=useQuery(['movies','trending'],moviesApi.trending

ex) useQuery() 의 경우

Object {
  "page": 1,
  "results": Array []
  }

    useInfiniteQuery 의 경우 

Object {
  "pageParams": Array [
    undefined,
  ],
  "pages": Array [
    Object {
      "dates": Object {
        "maximum": "2022-01-01",
        "minimum": "2021-12-05",
      },
      "page": 1,
      "results": Array []
	}
 ]

로 여기서 results 안에 우리가 필요한 내용들이 배열로 들어가 있다. 즉 동일하게 results를 가져오려면

data={upComingData.pages.map((page)=>{ return (page.results)}).flat()}

로 가져와야 된다. 마지막 flat()이 씌여진 이유는  [ [ ] ] pages에서 map을 사용하므로 배열안에 배열이 생기된다. 그러므로 [ [ page.result], [page.result], [page.result], [page.result], [page.result] ] 이런식의 자료 구조가 되고 flat()을 이용하여  [ page.result, page.result, page.result, page.result, page.result ] 로 만들어 준다. 즉, 정리하면 Flat list의 data안에는 Array 형태가 들어가야 되므로 flat()을 이용하여 배열내 배열구조에서 내부 배열값을 꺼내준다.

 

2-2) 실제 infinite scroll 구현하기 

-> 2-2-1) useInfiniteQuery 에서 다음 페이지 번호 구하기 / 없을 시 null 반환

   const {isLoading:upcomingLoading,
    data:upComingData,
    isRefetching:upcomingRefetching,
    
    // hasNextPage는 아래의 getNextPageParam에서 return으로 나오는 값이다. (boolean)
    // 현재 page에서 +1한 값이 전체페이지 보다 크게되면 null이므로 false반환 
    // 그렇지 않으면 nextPage 숫자값을 반환하므로 true이다. 
    hasNextPage,
    
    // fetchNextPage는 hasNextPage가 존재할 때 호출할 수 있는 함수로
    // refetch 처럼 다시 함수를 불러오는 것으로 쓰이는 듯
    fetchNextPage,
    
    }=useInfiniteQuery(['movies','upcoming'],moviesApi.upcoming,   
    
    	// useQuery()에서 사용한 option의 형태로 작성되며 여기서 return 된 값은
        // moviesApi.upcoming , fetch 함수 호출 시에 인자로 전달된다. 
        
        //여기서 last page는 useInfiniteQuery로 받는 data 전체를 의미한다.
        // 기본적으로 Api에서 page로 존재하는 숫자값에 +1 더해서 다음페이지가 될때
        // Api에 존재하는 total_pages 보다 작으면 다음페이지가 존재하는 것이고
        // 크거나 같으면 존재하지 않으므로 null을 반환
        
        {
            getNextPageParam: (lastPage) => {
                const nextPage = lastPage.page + 1;
                return nextPage > lastPage.total_pages ? null : nextPage;
            },
        }
    )

* getNextPagePram에서 lastPage에 받는 값은 위에서 useInfiniteQuery 에서 받은 data값이다.

Object {
  "pageParams": Array [
    undefined,
  ],
  "pages": Array [
    Object {
      "dates": Object {
        "maximum": "2022-01-01",
        "minimum": "2021-12-05",
      },
      "page": 1,
      "results": Array []
	}
 ]

 

2-2-2 ) useInfiniteQuery(['movies','upcoming'],moviesApi.upcoming,  에서 fetch Api인 moviesApi.upcoming를 불러올 때 getNextPageParam에서 return 값이 fetch 함수에 전달된다.

export const moviesApi = { 
    
    // 여기서 전달받은 pageParam은 useInfiniteQuery의 getNextPageParam에서보낸 return 값이다
    // 다음페이지가 있으면 그 숫자가 전달되고, 없으면 null이 전달된다.
    
    upcoming : ({pageParam}) =>
    fetch(
      `${BASE_URL}/movie/upcoming?api_key=${API_KEY}&language=ko-KR&page=${pageParam}`
    ).then((res) => res.json())
    
};

 

2-2-3) hasNextPage가 true일 시 fetchNextPage 함수 수행 

    // 위에서 getNextPageParam에서 숫자 전달 시 true가 되며, 이 때는 다음페이지가 존재한다는
    // 의미이므로 fetchNextPage() 함수를 실행시킨다.
    
    const loadMore=()=>{
        if (hasNextPage) {
            fetchNextPage();
          }
    };
    
    // 위에서 onEndReached로 끝에 도달하면 함수를 실행시키며
    //  onEndReachedThreshold를 두어서 어느 정도 도달하는 지 정의할 수 있다.
    
    <FlatList
        onEndReached={loadMore}
        onEndReachedThreshold={0.4}
      />

 

최종적으로 정리하면 useInfiniteQuery를 써서 data를 받아오고 pages 안에 있는 배열을 한개씩 늘려가는 형태로 무한스크롤을 구현한 것이다. useInfiniteQuery를 사용하여 getNextPageParam를 사용하여 다음페이지가 있는 지 없는 지 확인하고 여기서 return 된 값을 api함수의 {pageParam}로 받아온다. ( 변수명 바뀌면 안됨 )

이 값이 null이 아닐 경우 fetchNextPage를 통해서 다음 page를 받아온다. 

* api에서 page와 total_page 정보가 있는 경우 이를 사용할 수 있을 듯 

// 처음
/*{
  "pageParams": [undefined],
  "pages": [
    {"dates": [Object], "page": 1, "results": [Array], "total_pages": 18, "total_results": 348}
    ]
}*/


// 두번쨰 next page 불러올 때 
/*{
  "pageParams": [undefined],
  "pages": [
    {"dates": [Object], "page": 1, "results": [Array], "total_pages": 18, "total_results": 348}
    {"dates": [Object], "page": 2, "results": [Array], "total_pages": 18, "total_results": 348}
    ]
}*/


// 세번쨰 next page 불러올 때 
/*{
  "pageParams": [undefined],
  "pages": [
    {"dates": [Object], "page": 1, "results": [Array], "total_pages": 18, "total_results": 348}
    {"dates": [Object], "page": 2, "results": [Array], "total_pages": 18, "total_results": 348}
    {"dates": [Object], "page": 3, "results": [Array], "total_pages": 18, "total_results": 348}
    ]
}*/
반응형
Comments