您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~
列表组件的封装其实和Table有一定的相似,都是数据一列一列展示,因此列表组件也用到了懒加载和虚拟列表对大数据量时提供了解决方案。
组件库文档:
List.tsx:
import React, {
createContext, useMemo, useState, useEffect, useRef } from 'react';
import {
listProps, listHeaderStyle, listContentStyle } from './interface';
import Item from './item';
import './style/list.module.less';
export const ctx = createContext<any>({
} as any); //顶层通信装置
const List = (props: listProps) => {
const {
style = {
},
dataSource,
render,
header,
size = 'default',
lazyLoad = false,
defaultShowNum = 5,
virtualListProps,
virtualShowNum = 5,
} = props;
const contextProps = {
size,
};
const [formatDataSrouce, setFormatDataSource] = useState([...dataSource]); //处理过的数据源
const [scrollTop, setScrollTop] = useState(0);
const listItemHeight = useRef<any>(null);
const listContentRef = useRef<any>(null);
const victurlListContentRef = useRef<any>(null);
useEffect(() => {
if (lazyLoad && defaultShowNum) {
setFormatDataSource((old) => {
old = dataSource.slice(0, defaultShowNum);
return [...old];
});
} else if (virtualListProps) {
let rowHeight = document.querySelector('.list-item')?.clientHeight as any;
switch (size) {
case 'default':
rowHeight += 26;
break;
case 'small':
rowHeight += 18;
break;
case 'large':
rowHeight += 34;
break;
}
listItemHeight.current = rowHeight;
setFormatDataSource((old) => {
old = dataSource.slice(0, virtualShowNum + 2);
return [...old];
});
}
}, []);
const listHeaderStyle = useMemo(() => {
//头部样式
const defaultStyles: listHeaderStyle = {
};
switch (size) {
case 'default':
defaultStyles.padding = '12px 20px';
break;
case 'small':
defaultStyles.padding = '8px 20px';
break;
case 'large':
defaultStyles.padding = '16px 20px';
break;
}
return defaultStyles;
}, [size]);
const listStyle = useMemo(() => {
//表整体样式
return style;
}, [style]);
const listContentStyle = useMemo(() => {
//表正文样式
const returnStyle: listContentStyle = {
};
if (lazyLoad && defaultShowNum) {
returnStyle.height = '400px';
returnStyle.overflow = 'scroll';
}
return returnStyle;
}, [lazyLoad, defaultShowNum]);
const scrollList = () => {
const {
scrollHeight, clientHeight, scrollTop } = listContentRef.current as any;
const bottomTran = scrollHeight - clientHeight - scrollTop; //距离底部距离
if (bottomTran === 0) {
setTimeout(() => {
setFormatDataSource((old) => {
old = dataSource.slice(0, old.length + defaultShowNum);
return [...old];
});
}, 500);
}
};
const victurlScroll = () => {
const startIndex = Math.floor(victurlListContentRef.current.scrollTop / listItemHeight.current);
setScrollTop(victurlListContentRef.current.scrollTop);
setFormatDataSource((old) => {
old = dataSource.slice(startIndex, startIndex + virtualShowNum + 2);
return [...old];
});
};
return (
<ctx.Provider value={
contextProps}>
<div className="rList" style={
listStyle}>
<div className="list-header" style={
listHeaderStyle}>
{
header}
</div>
{
virtualListProps ? (
<div
className="victurl-list-content"
style={
{
height: virtualShowNum * listItemHeight.current + 'px' }}
ref={
victurlListContentRef}
onScroll={
victurlScroll}
>
<div
className="victurl-relly-content"
style={
{
height: dataSource.length * listItemHeight.current - scrollTop + 'px',
transform: `translate(0, ${
scrollTop}px)`,
}}
>
{
formatDataSrouce.map(render)}
</div>
</div>
) : (
<div
className="list-content"
style={
listContentStyle}
ref={
listContentRef}
onScroll={
scrollList}
>
{
formatDataSrouce.map(render)}
</div>
)}
</div>
</ctx.Provider>
);
};
interface ForwardRefListType
extends React.ForwardRefExoticComponent<
React.PropsWithoutRef<listProps> & React.RefAttributes<HTMLDivElement>
> {
<T = any>(
props: React.PropsWithChildren<listProps<T>> & {
ref?: React.Ref<HTMLDivElement>;
},
): React.ReactElement;
Item: typeof Item;
}
const ListComponent = React.forwardRef<HTMLDivElement, listProps>(List) as ForwardRefListType;
ListComponent.Item = Item;
ListComponent.displayName = 'List';
export default ListComponent;
Item.tsx:
import React, {
FC, memo, useMemo, useContext } from 'react';
import {
listItemProps } from './interface';
import {
ctx } from './index';
import './style/item.module.less';
const Item: FC<listItemProps> = (props) => {
const {
children, style = {
} } = props;
const {
size } = useContext(ctx);
const listItemStyle = useMemo(() => {
const defaultStyles = style;
switch (size) {
case 'default':
defaultStyles.padding = '13px 20px';
break;
case 'small':
defaultStyles.padding = '9px 20px';
break;
case 'large':
defaultStyles.padding = '17px 20px';
break;
}
return defaultStyles;
}, [size]);
return (
<div className="list-item" style={
listItemStyle}>
{
children}
</div>
);
};
export default memo(Item);
最后留一下React-View-UI组件库线上地址吧~
- Concis组件库线上链接:http://react-view-ui.com:92
- github:https://github.com/fengxinhhh/Concis
- npm:https://www.npmjs.com/package/concis
开源不易,欢迎学习和体验,喜欢请多多支持,有问题请留言。