Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/Body/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ResizeContext from '../context/ResizeContext';
import BodyRow from './BodyRow';
import useFlattenRecords from '../hooks/useFlattenRecords';
import HoverContext from '../context/HoverContext';
import PerfContext, { PerfRecord } from '../context/PerfContext';
import MeasureRow from './MeasureRow';

export interface BodyProps<RecordType> {
Expand Down Expand Up @@ -38,6 +39,11 @@ function Body<RecordType>({
const flattenData: { record: RecordType; indent: number; index: number }[] =
useFlattenRecords<RecordType>(data, childrenColumnName, expandedKeys, getRowKey);

// =================== Performance ====================
const perfRef = React.useRef<PerfRecord>({
renderWithProps: false,
});

// ====================== Hover =======================
const [startRow, setStartRow] = React.useState(-1);
const [endRow, setEndRow] = React.useState(-1);
Expand Down Expand Up @@ -132,7 +138,11 @@ function Body<RecordType>({
flattenData,
]);

return <HoverContext.Provider value={hoverContext}>{bodyNode}</HoverContext.Provider>;
return (
<PerfContext.Provider value={perfRef.current}>
<HoverContext.Provider value={hoverContext}>{bodyNode}</HoverContext.Provider>
</PerfContext.Provider>
);
}

const MemoBody = React.memo(Body);
Expand Down
68 changes: 46 additions & 22 deletions src/Cell/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import StickyContext from '../context/StickyContext';
import HoverContext from '../context/HoverContext';
import type { HoverContextProps } from '../context/HoverContext';
import warning from 'rc-util/lib/warning';
import PerfContext from '../context/PerfContext';

/** Check if cell is in hover range */
function inHoverRange(cellStartRow: number, cellRowSpan: number, startRow: number, endRow: number) {
Expand Down Expand Up @@ -120,19 +121,23 @@ function Cell<RecordType extends DefaultRecordType>(
): React.ReactElement {
const cellPrefixCls = `${prefixCls}-cell`;

const perfRecord = React.useContext(PerfContext);
const supportSticky = React.useContext(StickyContext);

// ==================== Child Node ====================
let cellProps: CellType<RecordType>;
let childNode: React.ReactNode;
const [childNode, legacyCellProps] = React.useMemo<
[React.ReactNode, CellType<RecordType>] | [React.ReactNode]
>(() => {
if (validateValue(children)) {
return [children];
}

if (validateValue(children)) {
childNode = children;
} else {
const value = getPathValue<object | React.ReactNode, RecordType>(record, dataIndex);

// Customize render node
childNode = value;
let returnChildNode = value;
let returnCellProps: CellType<RecordType> | undefined = undefined;

if (render) {
const renderData = render(value, record, renderIndex);

Expand All @@ -143,25 +148,41 @@ function Cell<RecordType extends DefaultRecordType>(
'`columns.render` return cell props is deprecated with perf issue, please use `onCell` instead.',
);
}
childNode = renderData.children;
cellProps = renderData.props;
returnChildNode = renderData.children;
returnCellProps = renderData.props;
perfRecord.renderWithProps = true;
} else {
childNode = renderData;
returnChildNode = renderData;
}
}
}

return [returnChildNode, returnCellProps];
}, [
/* eslint-disable react-hooks/exhaustive-deps */
// Always re-render if `renderWithProps`
perfRecord.renderWithProps ? Math.random() : 0,
/* eslint-enable */
children,
dataIndex,
perfRecord,
record,
render,
renderIndex,
]);

let mergedChildNode = childNode;

// Not crash if final `childNode` is not validate ReactNode
if (
typeof childNode === 'object' &&
!Array.isArray(childNode) &&
!React.isValidElement(childNode)
typeof mergedChildNode === 'object' &&
!Array.isArray(mergedChildNode) &&
!React.isValidElement(mergedChildNode)
) {
childNode = null;
mergedChildNode = null;
}

if (ellipsis && (lastFixLeft || firstFixRight)) {
childNode = <span className={`${cellPrefixCls}-content`}>{childNode}</span>;
mergedChildNode = <span className={`${cellPrefixCls}-content`}>{mergedChildNode}</span>;
}

const {
Expand All @@ -170,7 +191,7 @@ function Cell<RecordType extends DefaultRecordType>(
style: cellStyle,
className: cellClassName,
...restCellProps
} = cellProps || {};
} = legacyCellProps || {};
const mergedColSpan = (cellColSpan !== undefined ? cellColSpan : colSpan) ?? 1;
const mergedRowSpan = (cellRowSpan !== undefined ? cellRowSpan : rowSpan) ?? 1;

Expand Down Expand Up @@ -220,10 +241,13 @@ function Cell<RecordType extends DefaultRecordType>(
let title: string;
const ellipsisConfig: CellEllipsisType = ellipsis === true ? { showTitle: true } : ellipsis;
if (ellipsisConfig && (ellipsisConfig.showTitle || rowType === 'header')) {
if (typeof childNode === 'string' || typeof childNode === 'number') {
title = childNode.toString();
} else if (React.isValidElement(childNode) && typeof childNode.props.children === 'string') {
title = childNode.props.children;
if (typeof mergedChildNode === 'string' || typeof mergedChildNode === 'number') {
title = mergedChildNode.toString();
} else if (
React.isValidElement(mergedChildNode) &&
typeof mergedChildNode.props.children === 'string'
) {
title = mergedChildNode.props.children;
}
}

Expand All @@ -248,7 +272,7 @@ function Cell<RecordType extends DefaultRecordType>(
[`${cellPrefixCls}-ellipsis`]: ellipsis,
[`${cellPrefixCls}-with-append`]: appendNode,
[`${cellPrefixCls}-fix-sticky`]: (isFixLeft || isFixRight) && isSticky && supportSticky,
[`${cellPrefixCls}-row-hover`]: !cellProps && hovering,
[`${cellPrefixCls}-row-hover`]: !legacyCellProps && hovering,
},
additionalProps.className,
cellClassName,
Expand All @@ -262,7 +286,7 @@ function Cell<RecordType extends DefaultRecordType>(
return (
<Component {...componentProps}>
{appendNode}
{childNode}
{mergedChildNode}
</Component>
);
}
Expand Down
11 changes: 11 additions & 0 deletions src/context/PerfContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from 'react';

export interface PerfRecord {
renderWithProps: boolean;
}

const PerfContext = React.createContext<PerfRecord>({
renderWithProps: false,
});

export default PerfContext;
78 changes: 78 additions & 0 deletions tests/Hover.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,82 @@ describe('Table.Hover', () => {
wrapper.find('tbody td').at(1).simulate('mouseLeave');
expect(wrapper.exists('.rc-table-cell-row-hover')).toBeFalsy();
});

describe('perf', () => {
it('legacy mode should render every time', () => {
let renderTimes = 0;

const wrapper = mount(
createTable({
columns: [
{
render: () => {
renderTimes += 1;
return {
children: null,
};
},
},
],
}),
);

expect(wrapper.exists('.rc-table-cell-row-hover')).toBeFalsy();

// Hover 0-0
renderTimes = 0;
wrapper.find('tbody td').at(0).simulate('mouseEnter');
expect(wrapper.find('td.rc-table-cell-row-hover')).toHaveLength(1);
expect(renderTimes).toBe(1);

// Hover 0-1
renderTimes = 0;
wrapper.find('tbody td').at(1).simulate('mouseEnter');
expect(wrapper.find('td.rc-table-cell-row-hover')).toHaveLength(1);
expect(renderTimes).toBe(2);

// Mouse leave
renderTimes = 0;
wrapper.find('tbody td').at(1).simulate('mouseLeave');
expect(wrapper.exists('.rc-table-cell-row-hover')).toBeFalsy();
expect(renderTimes).toBe(1);
});

it('perf mode to save render times', () => {
let renderTimes = 0;

const wrapper = mount(
createTable({
columns: [
{
render: () => {
renderTimes += 1;
return null;
},
},
],
}),
);

expect(wrapper.exists('.rc-table-cell-row-hover')).toBeFalsy();

// Hover 0-0
renderTimes = 0;
wrapper.find('tbody td').at(0).simulate('mouseEnter');
expect(wrapper.find('td.rc-table-cell-row-hover')).toHaveLength(1);
expect(renderTimes).toBe(0);

// Hover 0-1
renderTimes = 0;
wrapper.find('tbody td').at(1).simulate('mouseEnter');
expect(wrapper.find('td.rc-table-cell-row-hover')).toHaveLength(1);
expect(renderTimes).toBe(0);

// Mouse leave
renderTimes = 0;
wrapper.find('tbody td').at(1).simulate('mouseLeave');
expect(wrapper.exists('.rc-table-cell-row-hover')).toBeFalsy();
expect(renderTimes).toBe(0);
});
});
});