/** * 初始加载或者下拉加载的时候显示refreshing * 上拉加载更多不显示 */ import React, {Component, CSSProperties, PureComponent, ReactElement} from 'react'; import { createPagingResult, createReducerResult, dataToPagingResult, dataToReducerResult, PagingResult, ReducerResult } from "@yz1311/taro-state-view"; import InteractionManager from "../../utils/interactionManager"; import { View, ScrollView, Text } from '@tarojs/components'; import {AtActivityIndicator as ActivityIndicator} from 'taro-ui'; import './index.scss'; import TouchableOpacity from "../TouchableOpacity/TouchableOpacity"; export type IProps = { wrapperClassName?: string, wrapperStyle?: string | CSSProperties, listClassName?: string, listStyle?: string | CSSProperties, contentClassName?: string, contentStyle?: string | CSSProperties, data?: ReadonlyArray; /** * Takes an item from data and renders it into the list. Typical usage: * ``` * _renderItem = ({item}) => ( * this._onPress(item)}> * {item.title} * * ); * ... * * ``` * Provides additional metadata like `index` if you need it. */ renderItem: ({item: any, index: number}) => (React.ReactElement | null) | null | undefined; /** * 分页大小 */ pageSize?: number; totalCount: number, /** * 如果需要在外部调用,可以传入外部的该值 */ pagingResult?: PagingResult; loadData: (pageIndex: number, pageSize?: number) => Promise | Error>, clearData?: any, onPageIndexChange?: any, footerContainerStyle?: any | CSSProperties, footerTextStyle?: any | CSSProperties, refreshEnable: boolean; /** * 下拉刷新(refreshEnable为false时无效) */ onPullRefresh?: () => void; onPagingResultChange?: (result: PagingResult) => void; ListHeaderComponent?: Function | ReactElement | Component, /** * 挂载列表的时候,自动加载数据,默认:true */ autoload?: boolean; /** * 自动挂载列表的方式 * pull下拉刷新,跟手动下拉刷新的效果一致(refreshEnable为false时无效) * mute隐藏,只调用接口 */ autoloadType: 'pull' | 'mute' }; export interface IState { isRefreshing: boolean; pagingResult: PagingResult; //可能需要显示第几页,所以在state中也加入该属性 pageIndex: number; refresherEnabled: boolean; } export default class YZFlatList extends PureComponent { //表示当前正在加载更多数据 isLoadingMore = false; pageIndex = 1; constructor(props) { super(props); this.state = { isRefreshing: false, pagingResult: createPagingResult(), pageIndex: 1, refresherEnabled: true }; } static defaultProps = { totalCount: -1, pageSize: 10, refreshEnable: true, autoload: true, autoloadType: 'mute' }; private flatList: any; componentDidMount() { //由于有AMStateView的存在,所以放在外面触发 if (this.props.autoload) { InteractionManager.runAfterInteractions(()=>{ switch (this.props.autoloadType) { case 'pull': this.onPullRefresh(); break; case 'mute': console.log('auto load') this.loadData(); break; } }); } } static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.pagingResult !== prevState.pagingResult) { if (nextProps.pagingResult) { return { pagingResult: nextProps.pagingResult }; } } // 否则,对于state不进行任何操作 return null; } componentWillUnmount() { const {clearData} = this.props; clearData && clearData(); } _onScroll = () => { // console.log('_onScroll'); //滑动一下就可以重新触发_onEndReach了 if (this.isLoadingMore) { this.isLoadingMore = false; } } /** * 调用接口刷新(pageIndex会被重置为1) */ onRefresh = async () => { await this.loadData(1); } /** * 触发下拉刷新 */ onPullRefresh = async ()=>{ //下拉刷新的时候不会再次触发下拉刷新 if (this.state.isRefreshing) { return; } this.props.onPullRefresh && this.props.onPullRefresh(); this.setState({ isRefreshing: true, }); await this.onRefresh(); this.setState({ isRefreshing: false, }); } _onEndReach = async () => { const {pagingResult: {noMore}} = this.state; console.log('YZFlatList _onEndReach noMore:' + noMore + ' isLoadingMore:' + this.isLoadingMore); if (!this.isLoadingMore) { //继续加载下面的 if (!noMore) { //必须要禁用,否则加载下一页数据的时候,页面会跳一下 this.setState({ refresherEnabled: false }); // onReachEnd&&onReachEnd(); await this.loadData(this.pageIndex); } } } render() { const {pagingResult: {dataList, noMore}, refresherEnabled} = this.state; const {ListHeaderComponent, wrapperClassName, wrapperStyle, listClassName, listStyle, renderItem, contentClassName, contentStyle} = this.props; return ( { //下拉会触发该方法,但是设置refresherTriggered也会触发该方法 if(this.isLoadingMore) { return; } this.onPullRefresh&&this.onPullRefresh(); }} onRefresherPulling={()=>{ }} scrollY={true} onScrollToLower={this._onEndReach} > {typeof ListHeaderComponent == 'function' ? ListHeaderComponent() : ListHeaderComponent } { dataList.map((item,index)=>{ return renderItem&&renderItem({ item, index }); }) } {this._renderFooter()} ); } _keyExtractor = (item, index) => { return index + ''; } _renderFooter = () => { const {footerContainerStyle, footerTextStyle} = this.props; const {pagingResult: {loadDataResult, noMore}} = this.state; let promptTitle = noMore ? '没有更多内容了' : '加载中...'; let textColor = '#666'; //不是第一页加载错误,则在底部footer显示重新加载按钮,点击并重新加载 let isNotFirstLoadError = (!this.isLoadingMore) && this.pageIndex > 1 && loadDataResult.error != undefined; if (isNotFirstLoadError) { promptTitle = '重新加载'; textColor = '#3092BE'; } return ( { if (!isNotFirstLoadError) { return; } this._onEndReach(); }} className="yz-list-view__bottom" style={footerContainerStyle}> {noMore || isNotFirstLoadError ? null : } {promptTitle} ); } loadData = async (pageIndex = 1, pageSize = this.props.pageSize) => { if (this.isLoadingMore) { return; } this.isLoadingMore = true; const {loadData} = this.props; let dataResult = await (loadData && loadData(pageIndex, pageSize)); this.isLoadingMore = false; let nextPagingResult = this.state.pagingResult; //说明报错了 if (dataResult instanceof Error) { nextPagingResult = { ...nextPagingResult, loadDataResult: dataToReducerResult(dataResult, this.pageIndex) } } else if (Array.isArray(dataResult)) { let pagingResult = dataToPagingResult(this.state.pagingResult.dataList, (dataResult || []) as Array, pageIndex, pageSize); nextPagingResult = pagingResult; //dataToPagingResult已对pageIndex做了判断,数据为空时不会自增 this.pageIndex = pagingResult.pageIndex; } else if(dataResult) { //说明返回的是PagingResult对象 if((dataResult as PagingResult).loadDataResult != undefined) { nextPagingResult = dataResult as PagingResult; //dataToPagingResult已对pageIndex做了判断,数据为空时不会自增 this.pageIndex = nextPagingResult.pageIndex; } else { console.warn('YZFlatList loadData return value not support simple object'); } } else { // console.warn('YZFlatList loadData must return value'); nextPagingResult = { ...this.state.pagingResult, loadDataResult: dataToReducerResult(dataResult) }; } if (this.state.pagingResult !== nextPagingResult) { this.setState({ pagingResult: nextPagingResult, }); this.props.onPagingResultChange && this.props.onPagingResultChange(nextPagingResult); } } onPageIndexChange = (pageIndex = this.pageIndex) => { const {onPageIndexChange} = this.props; onPageIndexChange && onPageIndexChange(pageIndex); } }