Skip to content

Commit

Permalink
refactor(table): reduce render time (#1732)
Browse files Browse the repository at this point in the history
  • Loading branch information
chaishi authored Nov 23, 2022
1 parent 92c21b7 commit c7fb2f1
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 140 deletions.
114 changes: 99 additions & 15 deletions src/table/BaseTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,21 @@ const BaseTable = forwardRef<BaseTableRef, BaseTableProps>((props, ref) => {
ellipsisOverlayClassName: props.size !== 'medium' ? sizeClassNames[props.size] : '',
};

const headUseMemoDependencies = [
resizable,
thWidthList,
isFixedHeader,
rowAndColFixedPosition,
isMultipleHeader,
spansAndLeafNodes,
thList,
columnResizeParams,
classPrefix,
props.bordered,
props.resizable,
props.size,
];

// 多级表头左边线缺失
const affixedLeftBorder = props.bordered ? 1 : 0;

Expand Down Expand Up @@ -388,20 +403,71 @@ const BaseTable = forwardRef<BaseTableRef, BaseTableProps>((props, ref) => {
{isVirtual && <div className={virtualScrollClasses.cursor} style={virtualStyle} />}
<table ref={tableElmRef} className={classNames(tableElmClasses)} style={tableElementStyles}>
{renderColGroup(false)}
{props.showHeader && <THead {...{ ...headProps, thWidthList: resizable ? thWidthList.current : {} }} />}
<TBody {...tableBodyProps} />
<TFoot
rowKey={props.rowKey}
isFixedHeader={isFixedHeader}
rowAndColFixedPosition={rowAndColFixedPosition}
footData={props.footData}
columns={spansAndLeafNodes?.leafColumns || columns}
rowAttributes={props.rowAttributes}
rowClassName={props.rowClassName}
thWidthList={thWidthList.current}
footerSummary={props.footerSummary}
rowspanAndColspanInFooter={props.rowspanAndColspanInFooter}
></TFoot>
{useMemo(() => {
if (!props.showHeader) return null;
return <THead {...{ ...headProps, thWidthList: resizable ? thWidthList.current : {} }} />;
// eslint-disable-next-line
}, headUseMemoDependencies)}

{useMemo(
() => (
<TBody {...tableBodyProps} />
),
// eslint-disable-next-line
[
tableBodyProps.classPrefix,
tableBodyProps.ellipsisOverlayClassName,
tableBodyProps.rowAndColFixedPosition,
tableBodyProps.showColumnShadow,
tableBodyProps.data,
tableBodyProps.columns,
tableBodyProps.tableElm,
tableBodyProps.tableContentElm,
tableBodyProps.tableWidth,
isWidthOverflow,
props.rowKey,
props.rowClassName,
props.rowAttributes,
props.loading,
props.empty,
props.fixedRows,
props.firstFullRow,
props.lastFullRow,
props.rowspanAndColspan,
props.scroll,
props.cellEmptyContent,
],
)}

{useMemo(
() => (
<TFoot
rowKey={props.rowKey}
isFixedHeader={isFixedHeader}
rowAndColFixedPosition={rowAndColFixedPosition}
footData={props.footData}
columns={spansAndLeafNodes?.leafColumns || columns}
rowAttributes={props.rowAttributes}
rowClassName={props.rowClassName}
thWidthList={thWidthList.current}
footerSummary={props.footerSummary}
rowspanAndColspanInFooter={props.rowspanAndColspanInFooter}
></TFoot>
),
// eslint-disable-next-line
[
isFixedHeader,
rowAndColFixedPosition,
spansAndLeafNodes,
columns,
thWidthList,
props.rowKey,
props.footData,
props.rowAttributes,
props.rowClassName,
props.footerSummary,
],
)}
</table>
</div>
);
Expand Down Expand Up @@ -431,7 +497,25 @@ const BaseTable = forwardRef<BaseTableRef, BaseTableProps>((props, ref) => {
<div ref={tableRef} className={classNames(dynamicBaseTableClasses)} style={{ position: 'relative', ...style }}>
{!!topContent && <div className={tableBaseClass.topContent}>{topContent}</div>}

{renderAffixedHeader()}
{useMemo(
() => renderAffixedHeader(),
// eslint-disable-next-line
[
// eslint-disable-next-line
...headUseMemoDependencies,
showAffixHeader,
tableWidth,
tableElmWidth,
affixHeaderRef,
affixedLeftBorder,
tableElmClasses,
tableElementStyles,
columns,
spansAndLeafNodes,
props.showHeader,
props.headerAffixedTop,
],
)}

{tableContent}

Expand Down
129 changes: 129 additions & 0 deletions src/table/Cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { MouseEvent } from 'react';
import classNames from 'classnames';
import isFunction from 'lodash/isFunction';
import get from 'lodash/get';
import TEllipsis from './Ellipsis';
import { BaseTableCellParams, RowspanColspan, TableRowData, TdBaseTableProps } from './type';
import { RowAndColFixedPosition } from './interface';
import { getColumnFixedStyles } from './hooks/useFixed';
import { TableClassName } from './hooks/useClassName';
import { formatClassNames } from './utils';
import { TooltipProps } from '../tooltip';

export interface RenderEllipsisCellParams {
cellNode: any;
tableElm?: HTMLDivElement;
columnLength: number;
classPrefix?: string;
overlayClassName?: string;
}

export interface CellProps {
cellParams: BaseTableCellParams<TableRowData>;
rowAndColFixedPosition: RowAndColFixedPosition;
columnLength: number;
dataLength: number;
cellSpans: RowspanColspan;
cellEmptyContent: TdBaseTableProps['cellEmptyContent'];
tableClassNames: TableClassName;
tableElm?: HTMLDivElement;
classPrefix?: string;
overlayClassName?: string;
rowspanAndColspan: TdBaseTableProps['rowspanAndColspan'];
onClick?: (e: MouseEvent<HTMLTableCellElement>) => void;
}

export function renderCell(
params: BaseTableCellParams<TableRowData>,
extra?: {
cellEmptyContent?: TdBaseTableProps['cellEmptyContent'];
},
) {
const { col, row, rowIndex } = params;
// support serial number column
if (col.colKey === 'serial-number') {
return rowIndex + 1;
}
if (isFunction(col.cell)) {
return col.cell(params);
}
if (isFunction(col.render)) {
return col.render({ ...params, type: 'cell' });
}
const r = col.cell || col.render || get(row, col.colKey);
// 0 和 false 属于正常可用之,不能使用兜底逻辑 cellEmptyContent
if (![undefined, '', null].includes(r)) return r;
if (extra?.cellEmptyContent) return extra.cellEmptyContent;
return r;
}

function renderEllipsisCell(cellParams: BaseTableCellParams<TableRowData>, params: RenderEllipsisCellParams) {
const { cellNode, tableElm, columnLength, classPrefix, overlayClassName } = params;
const { col, colIndex } = cellParams;
let content = isFunction(col.ellipsis) ? col.ellipsis(cellParams) : undefined;
if (typeof col.ellipsis === 'object' && isFunction(col.ellipsis.content)) {
content = col.ellipsis.content(cellParams);
}
let tooltipProps = {};
if (typeof col.ellipsis === 'object') {
tooltipProps = 'props' in col.ellipsis ? col.ellipsis.props : col.ellipsis || undefined;
}
const tableElement = tableElm;
let placement: TooltipProps['placement'] = colIndex === 0 ? 'top-left' : 'top';
placement = colIndex === columnLength - 1 ? 'top-right' : placement;
return (
<TEllipsis
placement={placement}
attach={tableElement ? () => tableElement : undefined}
popupContent={content}
tooltipProps={tooltipProps}
overlayClassName={overlayClassName}
classPrefix={classPrefix}
>
{cellNode}
</TEllipsis>
);
}

const Cell = (props: CellProps) => {
const { cellParams, tableClassNames, tableElm, columnLength, classPrefix, overlayClassName } = props;
const { col, colIndex, rowIndex } = cellParams;
const { cellSpans, dataLength, rowAndColFixedPosition, cellEmptyContent, rowspanAndColspan, onClick } = props;
const { tableColFixedClasses, tdEllipsisClass, tableBaseClass, tdAlignClasses, tableDraggableClasses } =
tableClassNames;

const cellNode = renderCell(cellParams, { cellEmptyContent });
const tdStyles = getColumnFixedStyles(col, colIndex, rowAndColFixedPosition, tableColFixedClasses);
const customClasses = formatClassNames(col.className, { ...cellParams, type: 'td' });
const classes = [
tdStyles.classes,
customClasses,
{
[tdEllipsisClass]: col.ellipsis,
[tableBaseClass.tdLastRow]: rowIndex + cellSpans.rowspan === dataLength,
[tableBaseClass.tdFirstCol]: colIndex === 0 && rowspanAndColspan,
[tdAlignClasses[col.align]]: col.align && col.align !== 'left',
// 标记可拖拽列
[tableDraggableClasses.handle]: col.colKey === 'drag',
},
];
const normalAttrs = isFunction(col.attrs) ? col.attrs({ ...cellParams, type: 'td' }) : col.attrs;
const attrs = { ...normalAttrs, rowSpan: cellSpans.rowspan, colSpan: cellSpans.colspan };
return (
<td
key={col.colKey || colIndex}
className={classNames(classes)}
style={tdStyles.style}
{...attrs}
onClick={onClick}
>
{col.ellipsis
? renderEllipsisCell(cellParams, { cellNode, tableElm, columnLength, classPrefix, overlayClassName })
: cellNode}
</td>
);
};

Cell.displayName = 'Cell';

export default Cell;
2 changes: 1 addition & 1 deletion src/table/EditableCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from './type';
import useGlobalIcon from '../hooks/useGlobalIcon';
import { TableClassName } from './hooks/useClassName';
import { renderCell } from './TR';
import { renderCell } from './Cell';
import { validate } from '../form/formModel';
import log from '../_common/js/log';
import { AllValidateResult } from '../form/type';
Expand Down
Loading

0 comments on commit c7fb2f1

Please sign in to comment.