在这篇技术笔记中,我们将探讨如何在 React Native 应用程序中使用 FlatList 组件实现下拉刷新和滚动刷新功能。作为额外内容,我们将研究如何通过改变不同的参数,如大小和颜色,来自定义 RefreshControl 组件。要在 React Native 应用中实现刷新功能,我们可以利用 FlatList 组件的 refreshing 属性以及 onRefresh 回调函数。通过将 refreshing 属性与一个状态变量绑定,我们可以实现在下拉刷新时显示刷新指示器,并在刷新完成后隐藏它。要实现滚动刷新功能,我们可以使用 onEndReached 回调函数来监听列表滚动到底部的事件,并在需要时加载更多数据。另外,RefreshControl 组件提供了许多自定义选项,我们可以根据应用的需求进行调整。例如,我们可以通过设置 colors 属性来改变刷新指示器的颜色,通过设置 progressBackgroundColor 属性来改变刷新指示器的背景色。通过灵活运用这些方法,我们可以为我们的 React Native 应用添加强大的刷新功能,提升用户体验。
先来看一下最终实现效果:

结合刷新功能
接下来,我们将把下拉刷新功能和滚动刷新功能整合到一个 FlatList 组件中。
首先,打开 screens/Combined.jsx 文件并导入所需的依赖项和组件:
// JavaScript
// screens/Combined.jsx
import { FlatList, RefreshControl, StyleSheet, Text, View } from 'react-native';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { FETCH_RESULTS, useFetchUser } from '../hooks/useFetchUser';
import { Item } from '../components/Item';
import { Loader } from '../components/Loader';
接下来,我们创建我们的组件,并在两个地方调用 useFetchUser Hook,用于顶部和底部的加载器,如下所示:
// JavaScript
// screens/Combined.jsx
const Combined = () => {
const flatListRef = useRef(null);
const {
isLoading: isLoadingTop,
success: successTop,
users: usersTop,
errorMessage: errorMessageTop,
getUsers: getUsersTop,
} = useFetchUser();
const {
isLoading: isLoadingBottom,
success: successBottom,
users: usersBottom,
errorMessage: errorMessageBottom,
getUsers: getUsersBottom,
} = useFetchUser();
return (
<View>
</View>
)
export default Combined;
在 JavaScript 中,我们可以通过在值后面加一个冒号来重命名一个解构的值:
const { isLoading } = useFetchUser();
// isLoading can be renamed to isLoadingTop
const { isLoading: isLoadingTop } = useFetchUser();
现在,我们定义 combinedUsers 、 currentPage 、 refreshing 和 isTop 状态:
// JavaScript // screens/Combined.jsx const [combinedUsers, setConfirmedUsers] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [refreshing, setRefreshing] = useState(false); const [isTop, setIsTop] = useState(false);
然后我们定义 loadMoreItem 函数:
// JavaScript
// screens/Combined.jsx
const loadMoreItem = () => {
setCurrentPage((prev) => prev + 1);
};
接下来,我们定义 onRefresh 回调和 scrollToItem 函数:
// JavaScript
// screens/Combined.jsx
const onRefresh = useCallback(() => {
setRefreshing(true);
setIsTop(true);
loadMoreItem();
}, []);
const scrollToItem = (index) => {
flatListRef.current.scrollToIndex({ index: index, animated: false });
};
然后,我们定义 useEffect 钩子来获取数据:
// JavaScript
// screens/Combined.jsx
useEffect(() => {
if (isTop) {
getUsersTop(currentPage, isTop);
} else {
getUsersBottom(currentPage, isTop);
}
}, [currentPage]);
在上述代码中,我们检查 isTop 的值何时发生变化,以确定每次 currentPage 状态改变时调用的函数。
然后,我们为下拉刷新和滚动到底部的 API 请求成功时定义动作:
// JavaScript
// screens/Combined.jsx
useEffect(() => {
if (successTop) {
setRefreshing(false);
setConfirmedUsers(usersTop);
if (combinedUsers.length > 0) {
scrollToItem(FETCH_RESULTS - 1);
}
}
}, [successTop]);
useEffect(() => {
if (successBottom) {
setConfirmedUsers(usersBottom);
}
}, [successBottom]);
接下来,我们像这样组合标记:
// JavaScript
// screens/Combined.jsx
<View>
<Text
style={{
textAlign: 'center',
paddingVertical: 10,
fontSize: 18,
fontWeight: '600',
}}
>
Combined Bidirectional FlatList
</Text>
{errorMessageTop ? <Text>{errorMessageTop}</Text> : null}
<FlatList
ref={flatListRef}
data={combinedUsers}
renderItem={Item}
keyExtractor={(item) => item?.email}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
// tintColor='red'
// colors={['red', 'green', 'blue']}
// progressBackgroundColor={'green'}
// title='Loading users...'
// titleColor='green'
// size={'large'}
// progressViewOffset={200}
// tintColor='transparent'
// colors={['transparent']}
// style={{ backgroundColor: 'transparent' }}
/>
}
maxToRenderPerBatch={FETCH_RESULTS}
// ListHeaderComponent={<Loader isLoading={refreshing} withText={true} />}
ListFooterComponent={<Loader isLoading={isLoadingBottom} />}
onEndReached={() => {
loadMoreItem();
setIsTop(false);
}}
onEndReachedThreshold={0}
ListEmptyComponent={<Loader isLoading />}
initialScrollIndex={0}
// Layout doesn't know the exact location of the requested element.
// Falling back to calculating the destination manually
onScrollToIndexFailed={({ index, averageItemLength }) => {
flatListRef.current?.scrollToOffset({
offset: index * averageItemLength,
animated: true,
});
}}
/>
{errorMessageBottom ? <Text>{errorMessageBottom}</Text> : null}
</View>
我们的组件应该看起来像这样:
// JavaScript
// screens/Combined.jsx
import { FlatList, RefreshControl, StyleSheet, Text, View } from 'react-native';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { FETCH_RESULTS, useFetchUser } from '../hooks/useFetchUser';
import { Item } from '../components/Item';
import { Loader } from '../components/Loader';
const Combined = () => {
const flatListRef = useRef(null);
const {
isLoading: isLoadingTop,
success: successTop,
users: usersTop,
errorMessage: errorMessageTop,
getUsers: getUsersTop,
} = useFetchUser();
const {
isLoading: isLoadingBottom,
success: successBottom,
users: usersBottom,
errorMessage: errorMessageBottom,
getUsers: getUsersBottom,
} = useFetchUser();
const [combinedUsers, setConfirmedUsers] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [refreshing, setRefreshing] = useState(false);
const [isTop, setIsTop] = useState(false);
const loadMoreItem = () => {
setCurrentPage((prev) => prev + 1);
};
const onRefresh = useCallback(() => {
setRefreshing(true);
setIsTop(true);
loadMoreItem();
}, []);
const scrollToItem = (index) => {
flatListRef.current.scrollToIndex({ index: index, animated: false });
};
useEffect(() => {
if (isTop) {
getUsersTop(currentPage, isTop);
} else {
getUsersBottom(currentPage, isTop);
}
}, [currentPage]);
useEffect(() => {
if (successTop) {
setRefreshing(false);
setConfirmedUsers(usersTop);
if (combinedUsers.length > 0) {
scrollToItem(FETCH_RESULTS - 1);
}
}
}, [successTop]);
useEffect(() => {
if (successBottom) {
setConfirmedUsers(usersBottom);
}
}, [successBottom]);
return (
<View>
<Text
style={{
textAlign: 'center',
paddingVertical: 10,
fontSize: 18,
fontWeight: '600',
}}
>
Combined Bidirectional FlatList
</Text>
{errorMessageTop ? <Text>{errorMessageTop}</Text> : null}
<FlatList
ref={flatListRef}
data={combinedUsers}
renderItem={Item}
keyExtractor={(item) => item?.email}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
// tintColor='red'
// colors={['red', 'green', 'blue']}
// progressBackgroundColor={'green'}
// title='Loading users...'
// titleColor='green'
// size={'large'}
// progressViewOffset={200}
// tintColor='transparent'
// colors={['transparent']}
// style={{ backgroundColor: 'transparent' }}
/>
}
maxToRenderPerBatch={FETCH_RESULTS}
// ListHeaderComponent={<Loader isLoading={refreshing} withText={true} />}
ListFooterComponent={<Loader isLoading={isLoadingBottom} />}
onEndReached={() => {
loadMoreItem();
setIsTop(false);
}}
onEndReachedThreshold={0}
ListEmptyComponent={<Loader isLoading />}
initialScrollIndex={0}
// Layout doesn't know the exact location of the requested element.
// Falling back to calculating the destination manually
onScrollToIndexFailed={({ index, averageItemLength }) => {
flatListRef.current?.scrollToOffset({
offset: index * averageItemLength,
animated: true,
});
}}
/>
{errorMessageBottom ? <Text>{errorMessageBottom}</Text> : null}
</View>
);
};
export default Combined;
const styles = StyleSheet.create({});
要查看我们构建的内容,请更新 App.js 文件,如下所示:
// JavaScript
// App.js
import { StyleSheet, Text, View } from 'react-native';
import React from 'react';
import Combined from './screens/Combined';
const App = () => {
return (
<View style={styles.container}>
<Combined />
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
marginVertical: 40,
marginHorizontal: 20,
},
});
我们的最终应用程序应该像图一那样运行。
小结
在本篇技术笔记中,我们着重研究了如何在 React Native 项目中实现下拉刷新和滚动刷新功能,以及如何将这些功能结合起来。我们还讨论了如何定制刷新控制组件。
希望通过本教程能够对大家将来在 React Native 项目中实现下拉刷新和滚动刷新功能提供参考,感谢阅读!