Monday, March 13, 2023
HomeReactImplementing Infinite Scroll with React Question and FlatList in React Native by...

Implementing Infinite Scroll with React Question and FlatList in React Native by Aman Mittal

Implementing Infinite Scroll with React Question and FlatList in React Native

Printed on Jan 30, 2022

12 min learn

Infinite Scrolling is a approach to implement pagination in cell gadgets. It is not uncommon amongst cell interfaces because of the restricted quantity of area. If you happen to use social media purposes like Instagram or Twitter, this implementation is usually used throughout these apps.

On this tutorial, let’s discover ways to implement an infinite scroll utilizing the FlatList element in React Native. To fetch information, we’ll use an actual REST API service offered by RAWG. It is without doubt one of the largest online game databases, they usually have a free tier in terms of utilizing their API for private or passion initiatives. React Question library will assist us make the method of fetching information so much smoother.



To observe this tutorial, please be sure to have the next instruments and utilities put in in your native improvement atmosphere and have entry to the companies talked about beneath:

  • Node.js model 12.x.x or above put in
  • Have entry to at least one package deal supervisor akin to npm or yarn or npx
  • RAWG API key

You may also examine the full supply code for this instance is at this GitHub repo.

Creating a brand new React Native app


To create a brand new React Native app, let’s generate a undertaking utilizing create-react-native-app command-line instrument. This instrument helps create common React Native apps, helps React Native Net, and you should utilize native modules. It’s at the moment being maintained by the superior Expo workforce.

Open up a terminal window and execute the next command:

npx create-react-native-app

What's your app named? infinite-scroll-with-react-query

How would you want to start out › Default new app

cd infinite-scroll-with-react-query

Then, let’s set up all of the dependencies that might be used to create the demo app. In the identical terminal window:

yarn add native-base react-query && expo set up react-native-safe-area-context react-native-svg

This command ought to obtain all of the required dependencies. To run the app in its vanilla state, you may execute both of the next instructions (relying on the cell OS you are utilizing). These instructions will construct the app.

Making a Residence Display


Let’s create a brand new listing known as /src. This listing will comprise all of the code associated to the demo app. Inside it, create a sub-directory known as /screens that may comprise the element file, HomeScreen.js.

On this file, let’s add some JSX code to show the title of the app display.

1import React from 'react';

2import { Field, Textual content, Divider } from 'native-base';


4export const HomeScreen = () => {

5 return (

6 <Field flex={1} safeAreaTop backgroundColor="white">

7 <Field top={16} justifyContent={'middle'} px={2}>

8 <Textual content fontSize={28} fontWeight={'600'} coloration={'emerald.500'}>

9 Discover Video games

10 </Textual content>

11 </Field>

12 <Divider />

13 </Field>

14 );


The Field element from NativeBase is a generic element. It comes with many props, a number of of them are to use SafeAreaView of the machine. The prop safeAreaTop applies padding from the highest of the machine’s display. One benefit of utilizing the NativeBase library is its built-in elements present props like dealing with protected space views.

Most NativeBase elements additionally use utility props for mostly used styled properties akin to justifyContent, backgroundColor, and so on., and shorthands for these utility props akin to px for padding horizontal.

Establishing suppliers


Each NativeBase and React Question libraries require their corresponding suppliers to arrange on the root of the app. Open the App.js file and add the next:

1import React from 'react';

2import { StatusBar } from 'expo-status-bar';

3import { NativeBaseProvider } from 'native-base';

4import { QueryClient, QueryClientProvider } from 'react-query';


6import { HomeScreen } from './src/screens/HomeScreen';


8const queryClient = new QueryClient();


10export default perform App() {

11 return (

12 <>

13 <StatusBar fashion="auto" />

14 <NativeBaseProvider>

15 <QueryClientProvider consumer={queryClient}>

16 <HomeScreen />

17 </QueryClientProvider>

18 </NativeBaseProvider>

19 </>

20 );


All of the suppliers should wrap the entry level or the primary display of the applying. Within the above snippet, there is just one display, so all of the suppliers are wrapping HomeScreen.

The QueryClientProvider element offers an occasion within the type of QueryClient that may be additional used to work together with the cache.

After modifying App.js, you’ll get the next output on a tool:


Add a Base URL to make use of RAWG REST API


If you wish to proceed studying this submit and construct together with the demo app, be sure to have entry to the API key on your RAWG account. As soon as you have carried out that, create a brand new file known as index.js contained in the /src/config listing. This file will export the bottom url of the API and API key.

1const BASE_URL = '';


3const API_KEY = 'XXXXXX';


5export { BASE_URL, API_KEY };

Change the Xs within the above snippet with your individual API key.

Fetching information from the API


To fetch the information, we’ll use JavaScript fetch API methodology. Create a brand new file known as index.js inside /src/api. It’s going to import the bottom url and the API key from the /config listing and expose a perform that fetches the information.

1import { BASE_URL, API_KEY } from '../config';


3export const gamesApi = {


5 fetchAllGames: () =>

6 fetch(`${BASE_URL}/video games?key=${API_KEY}`).then(res => {

7 return res.json();

8 })


Subsequent, within the HomeScreen.js file, import React Question hook known as useQuery. This hook accepts two arguments. The primary argument is a novel key. This secret’s a novel identifier within the type of a string. It tracks the results of the question and caches it.

The second argument is a perform that returns a promise. This promise is resolved when there may be information or throws an error when there’s something mistaken when fetching the information. We have already created the promise perform that fetches information asynchronously from the API’s base Url within the type of gamesApi.fetchAllGames(). Let’s import the gamesApi as nicely.

Contained in the HomeScreen, let’s name this hook to get the information.

1import React from 'react';

2import { Field, Textual content, FlatList, Divider, Spinner } from 'native-base';

3import { useQuery } from 'react-query';


5import { gamesApi } from '../api';


7export const HomeScreen = () => {

8 const { isLoading, information } = useQuery('video games', gamesApi.fetchAllGames);


10 const gameItemExtractorKey = (merchandise, index) => {

11 return index.toString();

12 };


14 const renderData = merchandise => {

15 return (

16 <Textual content fontSize="20" py="2">

17 {merchandise.merchandise.identify}

18 </Textual content>

19 );

20 };


22 return isLoading ? (

23 <Field

24 flex={1}

25 backgroundColor="white"

26 alignItems="middle"

27 justifyContent="middle"

28 >

29 <Spinner coloration="emerald.500" dimension="lg" />

30 </Field>

31 ) : (

32 <Field flex={1} safeAreaTop backgroundColor="white">

33 <Field top={16} justifyContent={'middle'} px={2}>

34 <Textual content fontSize={28} fontWeight={'600'} coloration={'emerald.500'}>

35 Discover Video games

36 </Textual content>

37 </Field>

38 <Divider />

39 <Field px={2}>

40 <FlatList

41 information={information.outcomes}

42 keyExtractor={gameItemExtractorKey}

43 renderItem={renderData}

44 />

45 </Field>

46 </Field>

47 );


Within the above snippet, take a word that React Question comes with the implementation of request states akin to isLoading. The isLoading state implies that there isn’t a information and is at the moment within the “fetching” state. To enhance the consumer expertise, whereas the isLoading state is true, a loading indicator or a spinner element will be displayed (as did within the above snippet utilizing the Spinner element from NativeBase).

Right here is the output after this step:


Including pagination to the API request


The useInfiniteQuery hook offered by the React Question library is a modified model of the useQuery hook. Along with the request states akin to isLoading and information, it makes use of a perform to get the following web page quantity utilizing getNextPageParam.

Within the case of RAWG REST API, the information fetch on every request incorporates the next keys:

  • rely: the overall rely of video games.
  • subsequent: the url to the following web page.
  • earlier: the url of the earlier web page. Is null if the present web page is first.
  • outcomes: the array of things on a person web page.

The important thing names subsequent, and earlier will rely on the response construction of the API request. Be sure that to examine your information response what are the important thing names and what are their values.

At the moment, the API request made within the /api/index.js file doesn’t think about the quantity of the present web page. Modify as proven beneath to fetch the information based mostly on the web page quantity.

1export const gamesApi = {


3 fetchAllGames: ({ pageParam = 1 }) =>

4 fetch(`${BASE_URL}/video games?key=${API_KEY}&web page=${pageParam}`).then(res => {

5 return res.json();

6 })


The addition &web page=${pageParam} within the above snippet is how the getNextPageParam perform will traverse to the following web page if the present web page quantity is handed within the request endpoint. Initially, the worth of pageParam is 1.

Utilizing useInfiniteQuery hook


Let’s import the useInfiniteQuery hook within the HomeScreen.js file.


2import { useInfiniteQuery } from 'react-query';

Subsequent, contained in the HomeScreen element, exchange the useQuery hook with the useInfiniteQuery hook as proven beneath. Together with the 2 arguments, the brand new hook may also comprise an object because the third argument. This object incorporates the logic to fetch the information from the following web page utilizing the getNextPageParam perform.

The perform retrieves the web page variety of the following web page. It accepts a parameter known as lastPage that incorporates the response of the final question. As per the response construction we mentioned earlier within the earlier part, examine the worth of lastPage.subsequent. If it’s not null, return the following web page’s quantity. Whether it is null, return the response from the final question.

1const { isLoading, information, hasNextPage, fetchNextPage } = useInfiniteQuery(

2 'video games',

3 gamesApi.fetchAllGames,

4 {

5 getNextPageParam: lastPage => {

6 if (lastPage.subsequent !== null) {

7 return lastPage.subsequent;

8 }


10 return lastPage;

11 }

12 }


Implementing infinite scroll on FlatList


Within the earlier snippet, the hasNextPage and fetchNextPage are important. The hasNextPage incorporates a boolean. Whether it is true, it signifies that extra information will be fetched. The fetchNextPage is the perform offered by the useInfiniteQuery to fetch the information of the following web page.

Add a deal with methodology contained in the HomeScreen element known as loadMore. This perform might be used on the FlatList prop known as onEndReached. This prop is named when the scroll place reaches a threshold worth.

1const loadMore = () => {

2 if (hasNextPage) {

3 fetchNextPage();

4 }


One other distinction between useInfiniteQuery and useQuery is that the previous’s response construction consists of an array of fetched pages within the type of information.pages. Utilizing JavaScript map perform, get the outcomes array of every web page.

Modify the FlatList element as proven beneath:


2 information={ page => web page.outcomes).flat()}

3 keyExtractor={gameItemExtractorKey}

4 renderItem={renderData}

5 onEndReached={loadMore}


Right here is the output after this step. Discover the scroll indicator on the right-hand facet of the display. As quickly because it reaches a bit beneath half of the listing, it repositions itself. This repositioning signifies that the information from the following web page is fetched by the useInfiniteQuery hook.


The default worth of the edge is 0.5. Which means that the loadMore will get triggered on the half-visible size of the listing. To switch this worth, you may add one other prop, onEndReachedThreshold. It accepts a price between 0 and 1, the place 0 is the tip of the listing.


2 information={ page => web page.outcomes).flat()}

3 keyExtractor={gameItemExtractorKey}

4 renderItem={renderData}

5 onEndReached={loadMore}

6 onEndReachedThreshold={0.3}


Show a spinner when fetching subsequent web page information


One other approach to improve the consumer expertise is when the tip of the listing is reached, and the information of the following web page continues to be being fetched (as an example, the community is weak). Whereas the app consumer waits for the information, it’s good to show a loading indicator.

The useInfiniteQuery hook offers a state known as isFetchingNextPage. Its worth might be true when the information from the following web page is fetched utilizing fetchNextPage.

Modify the HomeScreen element as proven beneath. The loading spinner renders when the worth of isFetchingNextPage is true. The ListFooterComponent on the FlatList element is used to show the loading indicator on the finish of the listing gadgets.

1export const HomeScreen = () => {

2 const { isLoading, information, hasNextPage, fetchNextPage, isFetchingNextPage } =

3 useInfiniteQuery('video games', gamesApi.fetchAllGames, {

4 getNextPageParam: lastPage => {

5 if (lastPage.subsequent !== null) {

6 return lastPage.subsequent;

7 }


9 return lastPage;

10 }

11 });


13 const loadMore = () => {

14 if (hasNextPage) {

15 fetchNextPage();

16 }

17 };


19 const renderSpinner = () => {

20 return <Spinner coloration="emerald.500" dimension="lg" />;

21 };


23 const gameItemExtractorKey = (merchandise, index) => {

24 return index.toString();

25 };


27 const renderData = merchandise => {

28 return (

29 <Field px={2} mb={8}>

30 <Textual content fontSize="20">{merchandise.merchandise.identify}</Textual content>

31 </Field>

32 );

33 };


35 return isLoading ? (

36 <Field

37 flex={1}

38 backgroundColor="white"

39 alignItems="middle"

40 justifyContent="middle"

41 >

42 <Spinner coloration="emerald.500" dimension="lg" />

43 </Field>

44 ) : (

45 <Field flex={1} safeAreaTop backgroundColor="white">

46 <Field top={16} justifyContent={'middle'} px={2}>

47 <Textual content fontSize={28} fontWeight={'600'} coloration={'emerald.500'}>

48 Discover Video games

49 </Textual content>

50 </Field>

51 <Divider />

52 <Field px={2}>

53 <FlatList

54 information={ page => web page.outcomes).flat()}

55 keyExtractor={gameItemExtractorKey}

56 renderItem={renderData}

57 onEndReached={loadMore}

58 onEndReachedThreshold={0.3}

59 ListFooterComponent={isFetchingNextPage ? renderSpinner : null}

60 />

61 </Field>

62 </Field>

63 );


Right here is the output:


Wrapping up


On this tutorial, you have efficiently carried out infinite scroll utilizing useInfiniteQuery from React Question. Utilizing this library for fetching and managing information inside a React Native app takes away a whole lot of ache factors. Be sure that to take a look at the Infinite Queries documentation right here.

You may also examine the full supply code for this instance is at this GitHub repo.

I am a software program developer and a technical author. On this weblog, I write about Technical writing, Node.js, React Native and Expo.

At the moment, working at Expo. Beforehand, I’ve labored as a Developer Advocate, and Senior Content material Developer with firms like Draftbit, Vercel and Crowdbotics.



Please enter your comment!
Please enter your name here

Most Popular

Recent Comments