美文网首页
二次封装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