美文网首页
二次封装react无限加载组件(一)

二次封装react无限加载组件(一)

作者: 一个被程序员耽误的厨师 | 来源:发表于2019-12-20 10:02 被阅读0次

    写在前面

    • 本组件使用了antd组件库里提供的List组件,需要对antd有所了解呀,小伙伴们

    看了一下文档,也看了相关代码,官方给出的List无限加载组件实在是不跟恭维呀!一团乱麻,根本没法维护,也不能复用。

    开始二次封装之旅

    需要掌握的技能:

    • dva的使用。
    • hook
    • 函数组件

    在这里我把List组件封装成一个无状态函数组件(无状态组件有明显优势),也先贴下组件代码

    import React, { Component, useState,  } from 'react';
    import { Skeleton, List, message, Avatar, Spin, Icon } from 'antd';
    import { Link } from 'dva/router';
    import { connect } from 'dva';
    
    import WindowScroller from 'react-virtualized/dist/commonjs/WindowScroller';
    import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
    import VList from 'react-virtualized/dist/commonjs/List';
    import InfiniteLoader from 'react-virtualized/dist/commonjs/InfiniteLoader';
    
    import styles from './index.module.less';
    import {classNames} from '../../utils'
    
    const IconText = ({ type, text }) => (
      <span>
        <Icon type={type} style={{ marginRight: 8 }} />
        {text}
      </span>
    );
    
    function PullLoadList({
        className,
        dataSource = [],
        fetchData,
        loading = false,
        isEnd = false,
    }){
        const loadedRowsMap = {};
    
        const handleInfiniteOnLoad = ({ startIndex, stopIndex }) => {
    
            for (let i = startIndex; i <= stopIndex; i++) {
            // 1 means loading
                loadedRowsMap[i] = 1;
            }
    
            // 到底了
            if(isEnd) return;
    
            //加载数据
            fetchData();
        };
    
        const isRowLoaded = ({ index }) => !!loadedRowsMap[index];
    
        const renderItem = ({ index, key, style }) => {
            const item = dataSource[index];
            return (
                <List.Item
                    key={key}
                    style = {style}
                >
                    <div className={styles.content_text}>
                        <div className={styles.left_box}>
                            <Link  to={`/details/${index}`} className={styles.title}>{item.title}</Link>
                            <div className={styles.content}>
                                {item.content}
                            </div>
                            <div className={styles.actions}>
                                <IconText type="star-o" text={item.star} key="list-vertical-star-o" />
                                <IconText type="like-o" text={item.like} key="list-vertical-like-o" />
                                <span>{item.date}</span>
                            </div>
                        </div>
                        <div className={styles.right_box}>
                            <img
                            alt="logo"
                            src={item.image}
                        />
                        </div>
                    </div>
                </List.Item>
            );
        };
    
        const vlist = ({ height, isScrolling, onChildScroll, scrollTop, onRowsRendered, width }) => (
            <VList
                autoHeight
                height={height}
                isScrolling={isScrolling}
                onScroll={onChildScroll}
                overscanRowCount={1}
                rowCount={dataSource.length}
                rowHeight={200}
                rowRenderer={renderItem}
                onRowsRendered={onRowsRendered}
                scrollTop={scrollTop}
                width={width}
            />
        );
        const autoSize = ({ height, isScrolling, onChildScroll, scrollTop, onRowsRendered }) => (
            <AutoSizer disableHeight>
                {({ width }) =>
                    vlist({
                        height,
                        isScrolling,
                        onChildScroll,
                        scrollTop,
                        onRowsRendered,
                        width,
                    })
                }
            </AutoSizer>
        );
    
        const infiniteLoader = ({ height, isScrolling, onChildScroll, scrollTop }) => (
            <InfiniteLoader
                isRowLoaded={isRowLoaded}
                loadMoreRows={handleInfiniteOnLoad}
                rowCount={dataSource.length}
            >
                {({ onRowsRendered }) =>
                    autoSize({
                        height,
                        isScrolling,
                        onChildScroll,
                        scrollTop,
                        onRowsRendered,
                    })
                }
            </InfiniteLoader>
        );
    
        return (
            <div className={styles.pullloadlist}>
                {
                    dataSource.length > 0 ?
                    <List 
                        itemLayout="vertical" 
                        size="large" 
                        className={classNames(className)}>
                        <WindowScroller>{infiniteLoader}</WindowScroller>
                    </List> : ''
                }
    
                <div className={styles.loading}>
                    {isEnd && !loading ? 
                        <span>已经到底了</span> : 
                        <span>正在加载... <Spin className="demo-loading" /></span>
                    }
                </div>
            </div>
            
        );
    }
    
    export default PullLoadList;
    

    使用方法如下

    • 需要传入如下参数,这里对hook不了解的小伙伴,可以自行了解下 hook
    // 数据源
        const {dataSource, pagination} = list;
        // 当前页数
        const [count, setCount] = useState(pagination.currentPage || 1);
        // loading状态
        const [loading, setLoading] = useState(false);
        // 是否还有数据
        const [isEnd, setIsEnd] = useState(false);
        // 组件参数
        const listProps = {
            isEnd: isEnd,
            loading: loading,
            dataSource : dataSource,
            fetchData : () =>{
                setLoading(true);
                // 更新当前页数
                setCount(count+1)
                // 异步加载数据
                dispatch({
                    type: 'list/effect:init',
                    payload: {
                        currentPage: count
                    }
                }).then(result  => {
                    console.log(pagination.total)
                    if(count >= pagination.totalPage){
                        setIsEnd(true);
                    }
                    setLoading(false);
                });
            }
        }
    
    
    • 使用组件
    <PullLoadList className={styles.list} {...listProps}/>
    

    附一张加载图

    image.png

    相关文章

      网友评论

          本文标题:二次封装react无限加载组件(一)

          本文链接:https://www.haomeiwen.com/subject/ljybnctx.html