Implementing Pagination and Pull-to-Refresh in React Native with a Custom Hook
In modern mobile apps, pagination and pull-to-refresh are essential features for handling large datasets efficiently and enhancing user experience. Here, we’ll walk through creating a custom pagination hook in React Native, coupled with pull-to-refresh functionality, making it easy to handle data fetching and seamless loading.
Pagination Hook and Pull-to-Refresh Implementation
Step 1: Set Up Initial State and Import Dependencies
Start by setting up the structure of your initial data, state variables, and importing necessary dependencies.
Expected API Response Format:
Our API response format is expected to be structured as shown below. This helps in managing pagination, tracking total results, and keeping the current page status.
{
data: [],
totalResult: 0,
status: true,
pageNo: 1,
totalPages: 1,
perPage: 10,
}
If your API format differs, ensure you modify the hook to adapt to your API response.
Step 2: Create the usePagination
Hook
Let’s define the usePagination
hook to manage pagination, loading, and pull-to-refresh states. This hook will fetch data from an API endpoint, keeping track of the current page, total results, and more.
usePagination.jsx
import React, { useState, useEffect, useCallback } from 'react';
const initialData = {
data: [],
totalResult: 0,
status: true,
pageNo: 1,
totalPages: 1,
};
const usePagination = () => {
const [initialLoader, setInitialLoader] = useState(true);
const [data, setData] = useState(initialData.data);
const [totalResult, setTotalResult] = useState(initialData.totalResult);
const [pageNo, setPageNo] = useState(initialData.pageNo);
const [totalPages, setTotalPages] = useState(initialData.totalPages);
const [refreshing, setRefreshing] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
// Fetch data for a given page
const fetchData = async (page, perPage = 10) => {
try {
const response = await fetch(`https://dummyjson.com/products?limit=${perPage}&skip=${page}`);
const resultOld = await response.json();
const result = {
data: resultOld?.products,
totalResult: resultOld?.total,
status: true,
pageNo: page,
totalPages: Math.ceil(resultOld?.total / perPage) || 10,
};
if (result.status) {
setData(page === 1 ? result.data : [...data, ...result.data]);
setTotalResult(result.totalResult);
setPageNo(result.pageNo);
setTotalPages(result.totalPages);
} else {
console.error('Failed to fetch data');
}
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setRefreshing(false);
setLoadingMore(false);
setInitialLoader(false);
}
};
useEffect(() => {
fetchData(pageNo);
}, []);
// Pull-to-refresh
const handleRefresh = useCallback(() => {
setRefreshing(true);
fetchData(1); // Refresh from the first page
}, []);
// Load more data
const loadMore = () => {
if (!loadingMore && pageNo < totalPages) {
setLoadingMore(true);
fetchData(pageNo + 1);
}
};
return {
data,
totalResult,
refreshing,
loadingMore,
handleRefresh,
loadMore,
initialLoader,
};
};
export default usePagination;
Step 3: Integrate the usePagination
Hook into Your Component
With the usePagination
hook ready, let’s integrate it into a React Native screen component, such as FeedsListScreen
. This component will utilize FlatList
for rendering the data with pagination and pull-to-refresh enabled.
FeedsListScreen Component:
import React from 'react';
import {
FlatList,
View,
ActivityIndicator,
RefreshControl,
} from 'react-native';
import usePagination from './usePagination';
const FeedsListScreen = () => {
const {
data,
totalResult,
refreshing,
loadingMore,
handleRefresh,
loadMore,
initialLoader,
} = usePagination();
const renderFooter = () => {
if (!loadingMore || data.length < 8) return null; // Show footer loader only for subsequent pages
return <ActivityIndicator animating size="large" />;
};
return initialLoader ? (
<ActivityIndicator size="large" />
) : (
<FlatList
data={data}
keyExtractor={(item) => item._id.toString()}
renderItem={({ item }) => (
<View>
{/* Customize your item view here */}
</View>
)}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
ListFooterComponent={renderFooter}
onEndReached={loadMore}
onEndReachedThreshold={0.1}
/>
);
};
export default FeedsListScreen;
Explanation of the Key Parts
-
Fetching Data with Pagination:
- The
fetchData
function fetches data based on the page number, adds it to the existing dataset, and updates total results and total pages. -
useEffect
triggers the initial data fetch on mount.
- The
-
Pull-to-Refresh:
-
handleRefresh
sets the page to the first page and callsfetchData
, effectively refreshing the list.
-
-
Loading More Data on Scroll:
-
loadMore
triggers only if more pages are available. It increments thepageNo
to fetch the next set of data, and updates the existing data with new entries.
-
-
Customizing UI with
renderFooter
:- This displays an
ActivityIndicator
at the bottom only when additional data is being loaded for better user feedback.
- This displays an
Final Thoughts
This approach encapsulates pagination and pull-to-refresh into a reusable custom hook, making it modular and easy to integrate into various components. By using React Native’s FlatList
and RefreshControl
along with this custom hook, you can seamlessly handle large datasets while providing a responsive user experience.
With usePagination
, managing paginated data and refreshing becomes straightforward, allowing for scalable, efficient data handling across your app.
Top comments (0)