React Native -- FlatList 之 下拉刷新,上拉加载
FlatList组件
import React, { FC, useEffect, useState, useRef } from 'react';
import { ActivityIndicator, FlatList as RNFlastList, FlatListProps } from 'react-native';
import { Text, View, ViewProps } from 'react-native-ui-lib';
import { ViewLoader, WidthSpace } from '..';
type IFlatListWithRematchProps<T = any> = FlatListProps<T> & {
flex?: boolean;
pageSize?: number; // 每页展示个数
onEndCB?: () => void; // 每次分页执行的回调
fetchDataFn: any;
currentPageNo: number;
totalCount: number;
pageToNextFn: any;
loading: boolean;
isNeedBigLoading: boolean;
triggerPageNo: number;
triggerListType?: string;
};
// 分页组件
const FlatList: FC<IFlatListWithRematchProps> = ({
flex = true,
pageSize = 10,
fetchDataFn, // 接口调用函数,封装为promise,执行.then处理接口返回的数据
pageToNextFn, // 上滑加载执行的函数
triggerPageNo, // 用于触发接口调用函数的执行,一旦监听到外部pageNo更新,接口调用函数重新执行
triggerListType, // 用于页面存在多个tab,在切换tab时,需要分页组件同样监听到外部pageNo更新,接口调用函数重新执行, 如果页面不存在tab则用不到这个参数
loading,
isNeedBigLoading = false, //是否在 ViewLoader上面加 loading
onEndCB, // 上滑加载完成后的额外执行函数
...restProps
}) => {
const [currentPageNo, setCurrentPageNo] = useState(1);
const [totalCount, setTotalCount] = useState(0);
const [results, setResults] = useState([]);
const totalPage = Math.ceil(totalCount / pageSize);
const flatListRef = useRef<any>();
useEffect(() => {
if (triggerPageNo > 0 && totalPage > 0 && triggerPageNo >= totalPage) return;
fetchDataFn.then((res: { pageNo: number; totalCount: number; results: [] }) => {
setCurrentPageNo(res.pageNo);
setTotalCount(res.totalCount);
setResults(res.results);
});
// 重新从头开始请求时,页面滚动到顶部
if (triggerPageNo === 0) {
flatListRef?.current?.scrollToOffset({ animated: false, offset: 0 });
}
return () => {
console.log('clear');
};
}, [triggerPageNo, triggerListType]);
useEffect(() => {
// 切换tab时,要求列表上滑到顶部
if (triggerListType) {
flatListRef?.current?.scrollToOffset({ animated: true, offset: 0 });
}
}, [triggerListType]);
const onEndReached = () => {
if (currentPageNo < totalPage) {
onNext();
onEndCB && onEndCB();
}
};
const onNext = () => {
pageToNextFn();
};
const emptyComponent = () => {
if (!loading && totalCount === 0) {
return (
<View center padding-30 paddingT-120>
<Text artTitle grey99>
暂无更多数据...
</Text>
</View>
);
}
return <View />;
};
const footerComponent = () => {
//console.log('footerComponent', currentPageNo, totalPage);
if (currentPageNo < totalPage || loading)
//
return (
<View row center padding-15>
<ActivityIndicator size="small" />
<WidthSpace />
<Text artTitle grey99>
正在加载中...
</Text>
</View>
);
if (currentPageNo === totalPage && currentPageNo !== 0 && totalPage > 0) {
return (
<View padding-15 center>
<Text artTitle grey99>
没有更多了...
</Text>
</View>
);
}
return <View></View>;
};
// loading={loading} 不使用View层的loading,分页统一使用分页自己的小loading
return (
<ViewLoader flex={flex} loading={isNeedBigLoading ? loading : false}>
<RNFlastList
ref={flatListRef}
data={results}
onEndReached={onEndReached}
onEndReachedThreshold={0.2}
ListFooterComponent={footerComponent}
ListEmptyComponent={emptyComponent}
{...restProps}
/>
</ViewLoader>
);
};
export const NoData: FC<ViewProps & { title?: string }> = ({
title = '暂无更多数据...',
...restProps
}) => (
<View center padding-30 {...restProps}>
<Text artTitle grey99>
{title}
</Text>
</View>
);
export const OnLoad: FC<ViewProps & { title?: string; loading?: boolean }> = ({
loading = false,
title = '正在加载中...',
}) => {
if (loading)
return (
<View row center padding-15>
<ActivityIndicator size="small" />
<WidthSpace />
<Text artTitle grey99>
{title}
</Text>
</View>
);
return <></>;
};
export default FlatList;
FlatListDemo
// 社区Tab下 动态Tab页
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
// @ts-ignore
import { RefreshNormalHeader } from 'react-native-smart-refresh';
import { View, Colors } from 'react-native-ui-lib';
import { DispatchPro, RootState } from '../../../store';
import { WidthSpace, LineSpace, FlatList } from '../../../components';
import { IDaynamicItem } from '../../../models/community/ICommunity.module';
import DaynamicItem from '../components/daynamicItem';
type ICommunity = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;
const Daynamic = (props: ICommunity) => {
const { fetchDaynamicCarousel, fetchAnnouncementList, fetchDaynamicList, daynamicLoading, shareStatus } = props;
const [daynamicList, setDaynamicList] = useState<IDaynamicItem[]>([]); // 动态列表数据
const [currentPageNo, setCurrentPageNo] = useState(-1); // 动态列表分页
// 页面初始化时,请求数据
const initRequest = () => {
fetchDaynamicCarousel({
params: {
pageTypeId: '1', // 页面类型;1-动态头部广告;2-文化每日分享运营位
},
apiName: 'fetchDaynamicCarousel',
});
fetchAnnouncementList({
params: {},
apiName: 'fetchAnnouncementList',
});
};
useEffect(() => {
initRequest();
setCurrentPageNo(0);
}, []);
useEffect(() => {
if (shareStatus === 'success') {
onRefresh();
}
}, [shareStatus]);
const onRefresh = () => {
setDaynamicList([]);
setCurrentPageNo(-1);
setTimeout(() => {
initRequest();
setCurrentPageNo(0);
}, 500);
};
// 请求动态列表数据,距离底部还有0.2 时触发 注意此参数是一个比值而非像素单位。比如,0.5 表示距离内容最底部的距离为当前列表可见长度的一半时触发。
const fetchDataFn = React.useMemo(() => new Promise((resolve) => {
if (currentPageNo < 0) return;
fetchDaynamicList
&& fetchDaynamicList({
params: { pageNo: currentPageNo + 1, pageSize: 10, pageTypeId: '1', associationId: '' },
apiName: 'fetchDaynamicList',
}).then(res => {
setDaynamicList([...daynamicList, ...(res?.shareInfoList || [])]);
resolve({
pageNo: currentPageNo + 1,
totalCount: res?.totalCount,
results: [...daynamicList, ...(res?.shareInfoList || [])],
});
});
}), [currentPageNo]);
const pageToNextFn = () => {
setCurrentPageNo(currentPageNo + 1);
};
return (
<FlatList
loading={daynamicLoading}
isNeedBigLoading
refreshControl={
<RefreshNormalHeader
refreshing={daynamicLoading}
onRefresh={onRefresh}
containerStyle={{
paddingLeft: 10,
paddingBottom: 30,
alignItems: 'flex-end',
}}
titleStyle={{ fontSize: 14 }}
timeStyle={{ display: 'none', fontSize: 14 }}
leftContainerStyle={{ marginBottom: -6, marginRight: -35 }}
activityIndicatorProps={{ color: Colors.primaryColor }}
/>
}
fetchDataFn={fetchDataFn}
pageToNextFn={pageToNextFn}
triggerPageNo={currentPageNo}
keyExtractor={(item: IDaynamicItem, index: number) => `${item.shareId}_${item.typeId}_${index}`}
renderItem={({ item }: { item: IDaynamicItem }) => {
return <View>
<LineSpace height={15} />
<View row>
<WidthSpace width={15} />
<DaynamicItem data={item} />
</View>
</View>;
}}
/>
);
};
const mapStateToProps = ({
community: { daynamicCarouselData, announcementSingleData, daynamicList, daynamicLoading, shareStatus },
}: RootState) => ({ daynamicCarouselData, announcementSingleData, daynamicList, daynamicLoading, shareStatus });
const mapDispatchToProps = ({
community: { fetchDaynamicCarousel, fetchAnnouncementList, fetchDaynamicList },
}: DispatchPro) => ({
fetchDaynamicCarousel,
fetchAnnouncementList,
fetchDaynamicList,
});
export default connect(mapStateToProps, mapDispatchToProps)(Daynamic);
浙公网安备 33010602011771号