react-native之上拉加载,下拉刷新组件封装

来源:互联网 发布:如何在php中定义常量 编辑:程序博客网 时间:2024/06/07 00:34

react-native 自定义封装刷新组件

几个月没写博客了,最近一直在写react 和react-native,前几天刚发了一版基于react-native混合开发的App,这几天赶快总结下。

写过java的同学,再去学习react和react-native就会比较容易上手,并且会有一种似曾相识的感觉,不错,今天我要总结的刷新功能就和Android原生的实现很类似,在Android原生当中,可以说是家喻户晓了,在react-native中实现也是一样的简单,在rn的升级中,FlatList是listView的升级版,就好比如RecyclerView是listView的升级版一样。OK, 废话不多话说。

目录

1.FlatList常用的属性方法

2.封装一个上拉加载,下拉刷新的组件


1.FlatList常用的属性方法

  1. data: ?Array 为了简化起见,data属性目前只支持普通数组。
  2. renderItem: (info: {item: ItemT, index: number}) => ?React.Element 根据行数据data渲染每一行的组件
  3. onRefresh?: ?() => void 如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能。同时你需要正确设置refreshing属性。
  4. refreshing?: ?boolean 在等待加载新数据时将此属性设为true,列表就会显示出一个正在加载的符号。
  5. onEndReachedThreshold?: ?number 决定当距离内容最底部还有多远时触发onEndReached回调。注意此参数是一个比值而非像素单位。比如,0.5表示距离内容最底部的距离为当前列表可见长度的一半时触发。
  6. onEndReached?: ?(info: {distanceFromEnd: number}) => void 当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用。
  7. ItemSeparatorComponent?: ?ReactClass 行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后。
  8. keyExtractor: (item: ItemT, index: number) => string 此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。
  9. ListHeaderComponent?: ?ReactClass 头部组件
  10. ListFooterComponent?: ?ReactClass 尾部组件
  11. ListEmptyComponent?: ?ReactClass | React.Element 列表为空时渲染该组件。可以是React Component, 也可以是一个render函数, 或者渲染好的element。
  12. extraData?: any 如果有除data以外的数据用在列表中(不论是用在renderItem还是Header或者Footer中),请在此属性中指定。同时此数据在修改时也需要先修改其引用地址(比如先复制到一个新的Object或者数组中),然后再修改其值,否则界面很可能不会刷新。

    注:当然还有其他的属性方法,请自行查阅文档FlatList

2.封装一个上拉加载,下拉刷新的组件

  1. 先定义一些propTypes,利于组件的扩展使用
static propTypes = {        renderItem: PropTypes.func.isRequired,    /* 渲染行的方法 */        ItemSeparatorComponent: PropTypes.func,   /* 渲染行间隔的方法 */        keyExtractor: PropTypes.func,             /* 返回每行key的方法 */        ListFooterComponent: PropTypes.func,      /* 渲染列表底部的方法 */        ListHeaderComponent: PropTypes.func,      /* 渲染列表头部的方法 */        listUrl: PropTypes.string.isRequired,     /* 获取列表的url */        fetchMethod: PropTypes.string,            /* 获取列表的请求方式(默认为get) */        fetchParams: PropTypes.object,            /* 获取列表的请求参数 */        pageSize: PropTypes.number,               /* 每页行数(默认20条) */        formatData: PropTypes.func,               /* 返回列表数组的方法 */        _ref: PropTypes.func,                     /* 获取组件实例属性 */        auto: PropTypes.bool,                     /* 是否在加载时自动查询(默认自动) */        showFooterBoo: PropTypes.bool,            /* 是否显示脚 */        ref: PropTypes.func,                      /* 获取组件实例 */    };

2.初始化当前状态state

constructor(props) {        super(props);        this._fetchUrl = props.listUrl;        this._fetchParams = props.fetchParams;        this.state = {            list: [],            /* 列表数据 */            pageNum: 1,          /* 当前页数 */            count: 0,            /* 总条数 */            refreshing: false,   /* 是否正在加载 */            isEnd: false,        /* 是否加载完 */            isNotList: false,    /* 是否未查询到数据 */            isError: false,      /* 是否查询失败 */        }    }

3.FlatList组件属性封装

  • 渲染前准备
const { list, refreshing } = this.state;        const {            renderItem,            ItemSeparatorComponent,            keyExtractor = (...arg) => arg[1],            ListHeaderComponent,            extraData = {},            showFooterBoo,        } = this.props;
  • FlatList封装
<FlatList                data={list}                renderItem={renderItem}                onRefresh={this.onRefresh}                refreshing={refreshing}                onEndReachedThreshold={0.2}                onEndReached={this.onEndReached}                ItemSeparatorComponent={ItemSeparatorComponent}                keyExtractor={keyExtractor}                ListHeaderComponent={this.renderListHeader}                ListFooterComponent={showFooterBoo ? this.renderListFooter : null}                ListEmptyComponent={this.renderListEmpty}                ref={ref => this._listRef = ref}                extraData={extraData}            />

4.data={list} 中list数据来源于引用组件处的属性

5.renderItem={renderItem} 中renderItem来源于引用组件的Item布局属性

6.onRefresh={this.onRefresh} 下拉刷新布局如下:

/**     * 下拉刷新     * @return {void}     */    onRefresh = () => {        this.setState({            isEnd: false,        }, () => {            this.getList();        });    }
/**     * 获取列表     * @return {void}     */    getList = (isRefresh = true) => {        const {            $fetch,            fetchMethod = 'get',            pageSize = 20,            formatData = ({data}) => ({                list: data.records,                count: data.total,            }),            fetchCatch = () => {},        } = this.props;        const { list, pageNum, refreshing, isEnd } = this.state;        if((refreshing || isEnd) && !isRefresh) return;        const fetchPageNum = isRefresh ? 1 : pageNum;        let fetchPromise;        if(fetchMethod.toLowerCase() === 'get') {            fetchPromise = $fetch.get(this._fetchUrl, {                size: pageSize,                current: fetchPageNum,                ...this._fetchParams,            });        } else if(fetchMethod.toLowerCase() === 'post') {            fetchPromise = $fetch.post(`${this._fetchUrl}`, {                ...this._fetchParams,            });        } else {            throw new Error('method type error! only "get" or "post"');        }        this.setState({refreshing: true});        fetchPromise.then(data => {            console.log(data);            if(!data.success) {                this.setState({ isError: true });                return;            }            const listData = formatData(data);            const currentList = isRefresh ? listData.list : list.concat(listData.list);            /* 判断是否查询到数据 */            if(fetchPageNum === 1 && listData.list.length === 0){                this.setState({                    isNotList: true                });            } else {                this.setState({                    isNotList: false                });            }            /* 如果有数据则添加到列表中 */            if(listData.list.length) {                this.setState({                    list: currentList,                    pageNum: fetchPageNum + 1,                });            }            if(currentList.length >= listData.count) {                this.setState({                    isEnd: true                });            }            this.setState({                count: listData.count,            });        }).catch(err => {            console.log(err.response);            if(!data.success) {                this.setState({ isError: true });            }            fetchCatch(err);        }).finally(() => {            this.setState({refreshing: false});        });    }

注:get与post可以根据自己的具体需求进行优化,其中网络请求使用的fetch。

7.refreshing={refreshing} 中refreshing布尔值,是用来判断当前是否是出于刷新状态的,当处于请求数据的刷新状态时置为true。

8.onEndReachedThreshold={0.2} 当滑动比例为0.2时触发onEndReached方法;onEndReached={this.onEndReached}上拉加载实现,如下:

/**     * 上拉加载     * @return {void}     */    onEndReached = () => {        this.getList(false);    }

注:this.getList(false);方法同下拉刷新,参数false代表上拉加载。

9.ItemSeparatorComponent={ItemSeparatorComponent} 自定义一个分割线,如下:

export const ItemSeparator = props => {    const {        paddingLeft = px(40),        backgroundColor = '#fff',        borderColor = '#eee',    } = props;    return (        <View style={{paddingLeft, backgroundColor}}>            <View style={{height: 1, backgroundColor: borderColor}}></View>        </View>    );};

10.keyExtractor={keyExtractor} 使用当前数据的id即可,如下:

keyExtractor={item => item.id}

11.ListHeaderComponent={this.renderListHeader} 头部的渲染,代码如下:

/**     * 渲染列表头部     */    renderListHeader = () => {        const { count } = this.state;        const { ListHeaderComponent = () => <View></View> } = this.props;        return ListHeaderComponent(this.state);    }

注:布局根据自己的需求进行定义即可。

12.ListFooterComponent={showFooterBoo ? this.renderListFooter : null} 属性showFooterBoo布尔值用来控制是否显示脚,脚的布局定义如下:

    /**     * 渲染列表底部     * @return {ReactComponent}     */    renderListFooter = () => {        const { ListFooterComponent = this.defaultListFooter } = this.props;        const { isEnd, refreshing } = this.state;        return this.defaultListFooter(isEnd, refreshing);    }
/**     * 列表默认底部组件     * @return {Node}     */    defaultListFooter = () => {        return (            <FlexView justify="center" align="center" style={styles.listFooterView}>                {this.defaultListFooterContent()}            </FlexView>        );    }
/**     * 列表底部显示内容     * @return {Node}     */    defaultListFooterContent = () => {        const { isEnd, refreshing, isNotList, isError } = this.state;        if(isNotList) {            return (                <_Text>未查询到相关数据!</_Text>            );        }        if(isEnd) {            return [                <FlexItem style={styles.listFooterLine} key={1}/>,                <_Text key={2}>没有更多数据</_Text>,                <FlexItem style={styles.listFooterLine} key={3}/>,            ];        }        if(refreshing) {            return (                <_Text>正在加载...</_Text>            );        }        if(isError) {            return (                <_Text>查询失败,下拉重新加载!</_Text>            );        }        return (            <_Text>上拉加载更多</_Text>        );    }

注:脚的布局可以根据自己的项目需求进行更改。

13.ListEmptyComponent={this.renderListEmpty} 空布局,没有数据,请求出错显示,代码如下:

/**     * 列表为空时显示的组件     * @return {ReactComponent}     */    renderListEmpty = () => {        return (            <View>                <Text>没有数据哦!</Text>            </View>        );    }

14.ref={ref => this._listRef = ref} 当前实例

ok,总结就到这里了,后续我会总结更多的使用组件的~~

阅读全文
0 0
原创粉丝点击