diff --git a/docs/demo/colspan-rowspan-legacy.md b/docs/demo/colspan-rowspan-legacy.md deleted file mode 100644 index 11809cef6..000000000 --- a/docs/demo/colspan-rowspan-legacy.md +++ /dev/null @@ -1,3 +0,0 @@ -## colspan-rowspan-legacy - - diff --git a/docs/demo/hover-perf.md b/docs/demo/hover-perf.md new file mode 100644 index 000000000..b15b020af --- /dev/null +++ b/docs/demo/hover-perf.md @@ -0,0 +1,3 @@ +## hover-perf + + diff --git a/docs/examples/colspan-rowspan-legacy.tsx b/docs/examples/colspan-rowspan-legacy.tsx deleted file mode 100644 index c67773d3c..000000000 --- a/docs/examples/colspan-rowspan-legacy.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react'; -import Table from 'rc-table'; -import '../../assets/index.less'; -import { ColumnsType, RenderedCell } from '@/interface'; - -interface RecordType { - a?: string; - b?: string; - c?: string; - d?: string; - e?: string; - key?: string; -} - -const columns: ColumnsType = [ - { - title: '手机号', - dataIndex: 'a', - colSpan: 2, - width: 100, - key: 'a', - render(o, row, index) { - const obj: RenderedCell = { - children: o, - props: {}, - }; - // 设置第一行为链接 - if (index === 0) { - obj.children = {o}; - } - // 第5行合并两列 - if (index === 4) { - obj.props.colSpan = 2; - } - - if (index === 5) { - obj.props.colSpan = 6; - } - return obj; - }, - }, - { - title: '电话', - dataIndex: 'b', - colSpan: 0, - width: 100, - key: 'b', - render(o, row, index) { - const obj: RenderedCell = { - children: o, - props: {}, - }; - // 列合并掉的表格设置colSpan=0,不会去渲染 - if (index === 4 || index === 5) { - obj.props.colSpan = 0; - } - return obj; - }, - }, - { - title: 'Name', - dataIndex: 'c', - width: 100, - key: 'c', - render(o, row, index) { - const obj: RenderedCell = { - children: o, - props: {}, - }; - - if (index === 5) { - obj.props.colSpan = 0; - } - return obj; - }, - }, - { - title: 'Address', - dataIndex: 'd', - width: 200, - key: 'd', - render(o, row, index) { - const obj: RenderedCell = { - children: o, - props: {}, - }; - if (index === 0) { - obj.props.rowSpan = 2; - } - if (index === 1 || index === 5) { - obj.props.rowSpan = 0; - } - - if (index === 5) { - obj.props.colSpan = 0; - } - - return obj; - }, - }, - { - title: 'Gender', - dataIndex: 'e', - width: 200, - key: 'e', - render(o, row, index) { - const obj: RenderedCell = { - children: o, - props: {}, - }; - if (index === 5) { - obj.props.colSpan = 0; - } - return obj; - }, - }, - { - title: 'Operations', - dataIndex: '', - key: 'f', - render(o, row, index) { - if (index === 5) { - return { - props: { - colSpan: 0, - }, - }; - } - return Operations; - }, - }, -]; - -const data: RecordType[] = [ - { a: '13812340987', b: '0571-12345678', c: '张三', d: '文一西路', e: 'Male', key: '1' }, - { a: '13812340986', b: '0571-98787658', c: '张夫人', d: '文一西路', e: 'Female', key: '2' }, - { a: '13812988888', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: '3' }, - { a: '1381200008888', b: '0571-099877', c: '王五', d: '文二西路', e: 'Male', key: '4' }, - { a: '0571-88888110', c: '李警官', d: '武林门', e: 'Male', key: '5' }, - { a: '资料统计完毕于xxxx年xxx月xxx日', key: '6' }, -]; - -const Demo = () => ( -
-

colSpan & rowSpan

- - -); - -export default Demo; diff --git a/src/Cell/index.tsx b/src/Cell/index.tsx index 5d8d2fe22..ddd2e9d81 100644 --- a/src/Cell/index.tsx +++ b/src/Cell/index.tsx @@ -5,9 +5,7 @@ import { supportRef } from 'rc-util/lib/ref'; import type { DataIndex, ColumnType, - RenderedCell, CustomizeComponent, - CellType, DefaultRecordType, AlignType, CellEllipsisType, @@ -17,7 +15,6 @@ 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'; import { useContextSelector } from '../ContextSelector'; /** Check if cell is in hover range */ @@ -26,9 +23,7 @@ function inHoverRange(cellStartRow: number, cellRowSpan: number, startRow: numbe return cellStartRow <= endRow && cellEndRow >= startRow; } -function isRenderCell( - data: React.ReactNode | RenderedCell, -): data is RenderedCell { +function isRenderCell(data: any) { return data && typeof data === 'object' && !Array.isArray(data) && !React.isValidElement(data); } @@ -139,53 +134,35 @@ function Cell( ): React.ReactElement { const cellPrefixCls = `${prefixCls}-cell`; - const perfRecord = React.useContext(PerfContext); const supportSticky = React.useContext(StickyContext); // ==================== Child Node ==================== - const [childNode, legacyCellProps] = React.useMemo< - [React.ReactNode, CellType] | [React.ReactNode] - >(() => { + const childNode = React.useMemo(() => { if (validateValue(children)) { - return [children]; + return children; } - const value = getPathValue | React.ReactNode, RecordType>( - record, - dataIndex, - ); + const value = getPathValue(record, dataIndex); // Customize render node - let returnChildNode = value; - let returnCellProps: CellType | undefined = undefined; - if (render) { - const renderData = render(value, record, renderIndex); - - if (isRenderCell(renderData)) { - if (process.env.NODE_ENV !== 'production') { - warning( - false, - '`columns.render` return cell props is deprecated with perf issue, please use `onCell` instead.', - ); - } - returnChildNode = renderData.children; - returnCellProps = renderData.props; - perfRecord.renderWithProps = true; - } else { - returnChildNode = renderData; + const renderNode = render(value, record, renderIndex); + if (process.env.NODE_ENV !== 'production' && isRenderCell(renderNode)) { + warning( + false, + '`column.render` do not support cell props any more, please use `onCell` instead.', + ); } + return renderNode; } - return [returnChildNode, returnCellProps]; + return value; }, [ /* eslint-disable react-hooks/exhaustive-deps */ // Always re-render if `renderWithProps` - perfRecord.renderWithProps ? Math.random() : 0, /* eslint-enable */ children, dataIndex, - perfRecord, record, render, renderIndex, @@ -206,15 +183,8 @@ function Cell( mergedChildNode = {mergedChildNode}; } - const { - colSpan: cellColSpan, - rowSpan: cellRowSpan, - style: cellStyle, - className: cellClassName, - ...restCellProps - } = legacyCellProps || {}; - const mergedColSpan = (cellColSpan !== undefined ? cellColSpan : colSpan) ?? 1; - const mergedRowSpan = (cellRowSpan !== undefined ? cellRowSpan : rowSpan) ?? 1; + const mergedColSpan = colSpan ?? 1; + const mergedRowSpan = rowSpan ?? 1; if (mergedColSpan === 0 || mergedRowSpan === 0) { return null; @@ -269,7 +239,6 @@ function Cell( ref: React.Ref; } = { title, - ...restCellProps, ...additionalProps, colSpan: mergedColSpan !== 1 ? mergedColSpan : null, rowSpan: mergedRowSpan !== 1 ? mergedRowSpan : null, @@ -286,12 +255,11 @@ function Cell( [`${cellPrefixCls}-ellipsis`]: ellipsis, [`${cellPrefixCls}-with-append`]: appendNode, [`${cellPrefixCls}-fix-sticky`]: (isFixLeft || isFixRight) && isSticky && supportSticky, - [`${cellPrefixCls}-row-hover`]: !legacyCellProps && hovering, + [`${cellPrefixCls}-row-hover`]: hovering, }, additionalProps.className, - cellClassName, ), - style: { ...additionalProps.style, ...alignStyle, ...fixedStyle, ...cellStyle }, + style: { ...additionalProps.style, ...alignStyle, ...fixedStyle }, onMouseEnter, onMouseLeave, ref: isRefComponent(Component) ? ref : null, diff --git a/src/Table.tsx b/src/Table.tsx index 51383a158..afe8491ba 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -78,6 +78,7 @@ import Summary from './Footer/Summary'; import StickyContext from './context/StickyContext'; import ExpandedRowContext from './context/ExpandedRowContext'; import { EXPAND_COLUMN } from './constant'; +import RenderContext from './context/RenderContext'; // Used for conditions cache const EMPTY_DATA = []; @@ -171,7 +172,7 @@ export interface TableProps sticky?: boolean | TableSticky; } -function Table(props: TableProps) { +function InternalTable(props: TableProps) { const { prefixCls, className, @@ -863,6 +864,15 @@ function Table(props: TableProps(props: TableProps) { + // Tell cell that update is from parent + return ( + + + + ); +} + Table.EXPAND_COLUMN = EXPAND_COLUMN; Table.Column = Column; diff --git a/src/context/RenderContext.tsx b/src/context/RenderContext.tsx new file mode 100644 index 000000000..fb5bad4fe --- /dev/null +++ b/src/context/RenderContext.tsx @@ -0,0 +1,5 @@ +import * as React from 'react'; + +const RenderContext = React.createContext(null); + +export default RenderContext; diff --git a/src/interface.ts b/src/interface.ts index a0d146537..45a9db5cb 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -48,11 +48,6 @@ export interface CellType { colEnd?: number; } -export interface RenderedCell { - props?: CellType; - children?: React.ReactNode; -} - export type DataIndex = string | number | readonly (string | number)[]; export type CellEllipsisType = { showTitle?: boolean } | boolean; @@ -76,11 +71,7 @@ export type AlignType = 'left' | 'center' | 'right'; export interface ColumnType extends ColumnSharedType { colSpan?: number; dataIndex?: DataIndex; - render?: ( - value: any, - record: RecordType, - index: number, - ) => React.ReactNode | RenderedCell; + render?: (value: any, record: RecordType, index: number) => React.ReactNode; shouldCellUpdate?: (record: RecordType, prevRecord: RecordType) => boolean; rowSpan?: number; width?: number | string; diff --git a/tests/Hover.spec.tsx b/tests/Hover.spec.tsx index 7f49160c3..5f851b632 100644 --- a/tests/Hover.spec.tsx +++ b/tests/Hover.spec.tsx @@ -43,7 +43,7 @@ describe('Table.Hover', () => { resetWarned(); const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - const wrapper = mount( + mount( createTable({ columns: [ { @@ -67,23 +67,8 @@ describe('Table.Hover', () => { }), ); - // Merge row check - expect(wrapper.find('tbody td')).toHaveLength(3); - - // Hover 0-0 - wrapper.find('tbody td').at(0).simulate('mouseEnter'); - expect(wrapper.find('td.rc-table-cell-row-hover')).toHaveLength(2); - - // Hover 0-1 - wrapper.find('tbody td').at(1).simulate('mouseEnter'); - expect(wrapper.find('td.rc-table-cell-row-hover')).toHaveLength(1); - - // Mouse leave - wrapper.find('tbody td').at(1).simulate('mouseLeave'); - expect(wrapper.exists('.rc-table-cell-row-hover')).toBeFalsy(); - expect(errorSpy).toHaveBeenCalledWith( - 'Warning: `columns.render` return cell props is deprecated with perf issue, please use `onCell` instead.', + 'Warning: `column.render` do not support cell props any more, please use `onCell` instead.', ); errorSpy.mockRestore(); }); @@ -127,45 +112,6 @@ describe('Table.Hover', () => { }); 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; diff --git a/tests/Table.spec.js b/tests/Table.spec.js index 6bc5b80dc..d886e58e1 100644 --- a/tests/Table.spec.js +++ b/tests/Table.spec.js @@ -308,32 +308,18 @@ describe('Table.Basic', () => { dataIndex: 'firstName', key: 'firstName', colSpan: 2, - render: (text, record, index) => { - const obj = { - children: text, - props: {}, - }; - if (index === 0) { - obj.props.colSpan = 2; - } - return obj; - }, + onCell: (_, index) => ({ + colSpan: index === 0 ? 2 : null, + }), }, { title: '', dataIndex: 'lastName', key: 'lastName', colSpan: 0, - render: (text, record, index) => { - const obj = { - children: text, - props: {}, - }; - if (index === 0) { - obj.props.colSpan = 0; - } - return obj; - }, + onCell: (_, index) => ({ + colSpan: index === 0 ? 0 : null, + }), }, ]; const localData = [ @@ -348,13 +334,10 @@ describe('Table.Basic', () => { const columns = [ { dataIndex: 'key', - render: text => ({ - props: { - style: { background: 'red' }, - className: 'customize-render', - 'data-light': 'bamboo', - }, - children: text, + onCell: () => ({ + style: { background: 'red' }, + className: 'customize-render', + 'data-light': 'bamboo', }), }, ]; @@ -377,17 +360,10 @@ describe('Table.Basic', () => { title: 'Last Name', dataIndex: 'lastName', key: 'lastName', - render: (text, record, index) => { - const obj = { - children: text, - props: {}, + onCell: (_, index) => { + return { + rowSpan: index === 0 ? 2 : 0, }; - if (index === 0) { - obj.props.rowSpan = 2; - } else { - obj.props.rowSpan = 0; - } - return obj; }, }, ];