UNPKG

react-native-ultimate-listview

Version:

A high performance FlatList providing customised pull-to-refresh | auto-pagination & infinite-scrolling | gridview layout | swipeable-row. The truly ultimate version that I have done the most tricky part for you, just simply follow the instructions shown

518 lines (444 loc) 12.7 kB
import React, { Component } from 'react' import PropTypes from 'prop-types' import { ActivityIndicator, Dimensions, FlatList, RefreshControl, ScrollView, StyleSheet, Text, View } from 'react-native' import RefreshableScrollView from './refreshableScrollView' const { width, height } = Dimensions.get('window') const PaginationStatus = { firstLoad: 0, waiting: 1, allLoaded: 2 } export default class UltimateListView extends Component { static defaultProps = { initialNumToRender: 10, horizontal: false, firstLoader: true, scrollEnabled: true, onFetch: null, enableEmptySections: true, // Custom View header: null, item: null, paginationFetchingView: null, paginationAllLoadedView: null, paginationWaitingView: null, emptyView: null, separator: null, // Refreshable refreshable: true, refreshableMode: 'basic', // basic or advanced // RefreshControl refreshableTitle: null, refreshableColors: ['dimgray', 'tomato', 'limegreen'], refreshableProgressBackgroundColor: 'white', refreshableSize: undefined, refreshableTintColor: 'lightgray', customRefreshControl: null, // Advanced RefreshView refreshableTitlePull: 'Pull to refresh', refreshableTitleRefreshing: 'Loading...', refreshableTitleRelease: 'Release to load', customRefreshView: null, displayDate: false, dateFormat: 'yyyy-MM-dd hh:mm', dateTitle: 'Last updated: ', arrowImageSource: require('./downArrow.png'), arrowImageStyle: undefined, refreshViewStyle: undefined, dateStyle: undefined, refreshViewHeight: 80, // Pagination pagination: true, autoPagination: true, allLoadedText: 'End of List', // Spinner spinnerColor: undefined, fetchingSpinnerSize: 'large', waitingSpinnerSize: 'small', waitingSpinnerText: 'Loading...', // Pagination Button paginationBtnText: 'Load more...', // GridView numColumns: 1 } static propTypes = { initialNumToRender: PropTypes.number, horizontal: PropTypes.bool, firstLoader: PropTypes.bool, scrollEnabled: PropTypes.bool, onFetch: PropTypes.func, enableEmptySections: PropTypes.bool, // Custom ListView header: PropTypes.func, item: PropTypes.func, paginationFetchingView: PropTypes.func, paginationAllLoadedView: PropTypes.func, paginationWaitingView: PropTypes.func, emptyView: PropTypes.func, separator: PropTypes.func, // Refreshable refreshable: PropTypes.bool, refreshableMode: PropTypes.string, // RefreshControl refreshableTitle: PropTypes.string, refreshableColors: PropTypes.array, refreshableProgressBackgroundColor: PropTypes.string, refreshableSize: PropTypes.string, refreshableTintColor: PropTypes.string, customRefreshControl: PropTypes.func, // Advanced RefreshView refreshableTitlePull: PropTypes.string, refreshableTitleRefreshing: PropTypes.string, refreshableTitleRelease: PropTypes.string, customRefreshView: PropTypes.func, displayDate: PropTypes.bool, dateFormat: PropTypes.string, dateTitle: PropTypes.string, arrowImageSource: PropTypes.any, arrowImageStyle: PropTypes.object, refreshViewStyle: PropTypes.object, dateStyle: PropTypes.object, refreshViewHeight: PropTypes.number, // Pagination pagination: PropTypes.bool, autoPagination: PropTypes.bool, allLoadedText: PropTypes.string, // Spinner spinnerColor: PropTypes.string, fetchingSpinnerSize: PropTypes.any, waitingSpinnerSize: PropTypes.any, waitingSpinnerText: PropTypes.string, // Pagination Button paginationBtnText: PropTypes.string, // GridView numColumns: PropTypes.number } constructor(props) { super(props) this.setPage(1) this.setRows([]) this.state = { dataSource: [], isRefreshing: false, paginationStatus: PaginationStatus.firstLoad } } componentDidMount() { this.mounted = true if (this.props.firstLoader) { this.props.onFetch(this.getPage(), this.postRefresh, this.endFetch) } } componentWillUnmount() { this.mounted = false } onRefresh = () => { console.log('onRefresh()') if (this.mounted) { this.setState({ isRefreshing: true }) this.setPage(1) this.props.onFetch(this.getPage(), this.postRefresh, this.endFetch) } } onPaginate = () => { if (this.state.paginationStatus !== PaginationStatus.allLoaded && !this.state.isRefreshing) { console.log('onPaginate()') this.setState({ paginationStatus: PaginationStatus.waiting }) this.props.onFetch(this.getPage() + 1, this.postPaginate, this.endFetch) } } onEndReached = () => { // console.log('onEndReached()'); if (this.props.pagination && this.props.autoPagination && this.state.paginationStatus === PaginationStatus.waiting) { this.onPaginate() } } setPage = page => this.page = page getPage = () => this.page setRows = rows => this.rows = rows getRows = () => this.rows sleep = time => new Promise(resolve => setTimeout(() => resolve(), time)) refresh = () => { this.onRefresh() } scrollToOffset = (option) => { this._flatList.scrollToOffset(option) } scrollToIndex = (option) => { this._flatList.scrollToIndex(option) } scrollToItem = (option) => { this._flatList.scrollToItem(option) } scrollToEnd = (option) => { this._flatList.scrollToEnd(option) } postRefresh = (rows = [], pageLimit) => { if (this.mounted) { let paginationStatus = PaginationStatus.waiting if (rows.length < pageLimit) { paginationStatus = PaginationStatus.allLoaded } this.updateRows(rows, paginationStatus) } } endFetch = () => { // console.log('endRefresh()'); if (this.mounted) { this.setState({ isRefreshing: false }) if (this.props.refreshableMode === 'advanced' && this._flatList._listRef._scrollRef.onRefreshEnd) { this._flatList._listRef._scrollRef.onRefreshEnd() } } } postPaginate = (rows = [], pageLimit) => { this.setPage(this.getPage() + 1) let mergedRows let paginationStatus if (rows.length === 0) { paginationStatus = PaginationStatus.allLoaded } else { mergedRows = this.getRows().concat(rows) paginationStatus = PaginationStatus.waiting } this.updateRows(mergedRows, paginationStatus) } updateRows = (rows, paginationStatus) => { if (rows) { this.setRows(rows) this.setState({ dataSource: rows, isRefreshing: false, paginationStatus }) } else { this.setState({ dataSource: this.getRows().slice(), isRefreshing: false, paginationStatus }) } if (this.props.refreshableMode === 'advanced') { this.endFetch() } } updateDataSource(rows = []) { this.setRows(rows) this.setState({ dataSource: rows }) } paginationFetchingView = () => { if (this.props.paginationFetchingView) { return this.props.paginationFetchingView() } return ( <View style={styles.fetchingView}> <Text style={styles.paginationViewText}>{this.props.waitingSpinnerText}</Text> </View> ) } paginationAllLoadedView = () => { if (this.props.pagination) { if (this.props.paginationAllLoadedView) { return this.props.paginationAllLoadedView() } return ( <View style={styles.paginationView}> <Text style={styles.allLoadedText}> {this.props.allLoadedText} </Text> </View> ) } return null } paginationWaitingView = (paginateCallback) => { if (this.props.pagination) { if (this.props.autoPagination) { if (this.props.paginationWaitingView) { return this.props.paginationWaitingView(paginateCallback) } return ( <View style={styles.paginationView}> <ActivityIndicator color={this.props.spinnerColor} size={this.props.waitingSpinnerSize} /> <Text style={[styles.paginationViewText, { marginLeft: 5 }]} >{this.props.waitingSpinnerText} </Text> </View> ) } } return null } renderHeader = () => { if (this.props.header) { return this.props.header() } return null } renderItem = ({ item, index, separators }) => { if (this.props.item) { return this.props.item(item, index, separators) } return null } renderSeparator = () => { if (this.props.separator) { if (this.props.numColumns > 1) { return null } return this.props.separator() } return null } renderEmptyView = () => { if (this.state.paginationStatus !== PaginationStatus.firstLoad && this.props.emptyView) { return this.props.emptyView() } return null } renderFooter = () => { if (this.state.paginationStatus === PaginationStatus.firstLoad) { return this.paginationFetchingView() } else if (this.state.paginationStatus === PaginationStatus.waiting && this.props.autoPagination === false) { return this.paginationWaitingView(this.onPaginate) } else if (this.state.paginationStatus === PaginationStatus.waiting && this.props.autoPagination === true) { return this.paginationWaitingView() } else if (this.getRows().length !== 0 && this.state.paginationStatus === PaginationStatus.allLoaded) { return this.paginationAllLoadedView() } return null } renderScrollComponent = (props) => { if (this.props.refreshable && this.props.refreshableMode === 'advanced') { return ( <RefreshableScrollView {...props} insideOfUltimateListView onRefresh={this.onRefresh} ref={ref => this.scrollView = ref} /> ) } return ( <ScrollView {...props} ref={ref => this.scrollView = ref} /> ) } renderRefreshControl = () => { if (this.props.refreshable && this.props.refreshableMode === 'basic') { if (this.props.customRefreshControl) { return this.props.customRefreshControl(this.state.isRefreshing, this.onRefresh) } return ( <RefreshControl onRefresh={this.onRefresh} refreshing={this.state.isRefreshing} colors={this.props.refreshableColors} progressBackgroundColor={this.props.refreshableProgressBackgroundColor} size={this.props.refreshableSize} tintColor={this.props.refreshableTintColor} title={this.props.refreshableTitle} /> ) } return null } render() { const { numColumns } = this.props return ( <FlatList renderScrollComponent={this.renderScrollComponent} key={numColumns} onEndReachedThreshold={0.1} {...this.props} ref={ref => this._flatList = ref} data={this.state.dataSource} renderItem={this.renderItem} ItemSeparatorComponent={this.renderSeparator} ListHeaderComponent={this.renderHeader} ListFooterComponent={this.renderFooter} ListEmptyComponent={this.renderEmptyView} onEndReached={this.onEndReached} refreshControl={this.renderRefreshControl()} numColumns={numColumns} /> ) } } const styles = StyleSheet.create({ fetchingView: { width, height, justifyContent: 'center', alignItems: 'center' }, paginationView: { flex: 0, width, height: 55, flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }, paginationViewText: { fontSize: 16 }, paginationViewSpinner: { marginRight: 5 }, paginationBtn: { backgroundColor: 'tomato', margin: 10, borderRadius: 20, flex: 1, height: 50, justifyContent: 'center', alignItems: 'center' }, paginationBtnText: { fontSize: 16, color: 'white', fontWeight: 'bold' }, separator: { height: 0.5, marginLeft: 15, marginRight: 15, backgroundColor: 'lightgray' }, emptyView: { flex: 1, justifyContent: 'center', alignItems: 'center' }, allLoadedText: { alignSelf: 'center', color: '#bfbfbf' }, gridItem: { overflow: 'hidden', alignItems: 'center', justifyContent: 'center' }, gridView: { flexDirection: 'row', alignItems: 'flex-start', flexWrap: 'wrap' } })