import React , { useState , useEffect , forwardRef , useImperativeHandle , Ref } from 'react' ; import { FlatList , FlatListProps , ListRenderItem } from 'react-native' ; import { emptyDataView , activityIndicatorView , noMoreDataView } from './View' ; interface QuickListProps < ItemT > extends FlatListProps < ItemT > { onFetch : ( params : any ) => void ; data : Array < any > ; renderItem : ListRenderItem < any > ; keyId : string | number ; emptyView ? : void | any ; pageSize ? : number ; total : number ; style ? : Object ; } export type QuickListComponent < ItemT = any > = ( props : QuickListProps < ItemT > , ref ? : Ref < FlatList < ItemT > > | any ) => React . ReactElement ; const QuickList = ( props , ref ) => { const { onFetch , data , renderItem = null , keyId , emptyView = emptyDataView , pageSize = 10 , total = 0 , style , ... others } = props ; const PaginationStatus = { firstLoad : 0 , waiting : 1 , allLoaded : 2 , } ; let mounted = true ; const [ endReachedTime , setEndReachedTime ] = useState ( Date . now ( ) ) ; const [ firstLoader , setFirstLoader ] = useState ( true ) ; const [ loading , setLoading ] = useState ( false ) ; const [ refreshing , setRefreshing ] = useState ( false ) ; const [ page , setPage ] = useState ( 1 ) ; const [ paginationStatus , setPaginationStatus ] = useState ( PaginationStatus . firstLoad ) ; useImperativeHandle ( ref , ( ) : any => ( { executeRefreshAction : async ( param = { } ) => { setPage ( 1 ) ; const params = { page : 1 , pageSize , ... param , } ; onFetch && ( await onFetch ( params ) ) ; } , } ) ) ; useEffect ( ( ) => { mounted = true ; firstLoader && firstLoad ( ) . catch ( ( error ) => { console . error ( error ) ; } ) ; return ( ) => { mounted = false ; } ; } , [ firstLoader ] ) ; useEffect ( ( ) => { if ( firstLoader || total === 0 ) { return ; } let maxPage = Math . ceil ( total / pageSize ) ; if ( maxPage === page ) { setPaginationStatus ( PaginationStatus . allLoaded ) ; } else { setPaginationStatus ( PaginationStatus . waiting ) ; } } , [ page , total , firstLoader , pageSize ] ) ; const renderFooter = ( ) => { if ( total === 0 ) { return null ; } else if ( paginationStatus === PaginationStatus . waiting ) { return activityIndicatorView ; } else if ( ! loading && paginationStatus === PaginationStatus . allLoaded && total !== 0 ) { return noMoreDataView ; } } ; const renderEmptyView = ( ) => { if ( ! firstLoader && typeof emptyView === 'function' ) { return emptyView ( ) ; } return null ; } ; const firstLoad = async ( ) => { if ( paginationStatus === PaginationStatus . firstLoad ) { setPage ( 1 ) ; setRefreshing ( true ) ; const params = { page : 1 , pageSize , isRefresh : true , } ; await onFetch ( params ) ; setRefreshing ( false ) ; setFirstLoader ( false ) ; } } ; const onRefresh = async ( ) => { if ( mounted && ! loading ) { setPage ( 1 ) ; setRefreshing ( true ) ; const params = { page : 1 , pageSize , isRefresh : true , } ; await onFetch ( params ) ; setRefreshing ( false ) ; } } ; const onEndReached = async ( ) => { if ( paginationStatus === PaginationStatus . firstLoad || paginationStatus === PaginationStatus . allLoaded || refreshing || loading ) { return ; } setLoading ( true ) ; const params = { page : page + 1 , pageSize , isRefresh : false , } ; await onFetch ( params ) ; setLoading ( false ) ; setPage ( page + 1 ) ; } ; return ( < FlatList { ... others } ref = { ref } onEndReachedThreshold = { 0.1 } data = { data } renderItem = { renderItem } refreshing = { refreshing } extraData = { data } keyExtractor = { ( item ) => item [ keyId ] } initialNumToRender = { pageSize } onRefresh = { onRefresh } onEndReached = { ( ) => { const now = Date . now ( ) ; if ( now - endReachedTime > 1000 ) { onEndReached && onEndReached ( ) ; setEndReachedTime ( now ) ; } } } ListFooterComponent = { renderFooter ( ) } ListEmptyComponent = { renderEmptyView ( ) } style = { style } / > ) ; } ; export default forwardRef ( QuickList ) ;