写在前面
- 本组件使用了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