diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 4d5fefaca..f4b61b7b8 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -1,8 +1,9 @@ -import { useContext } from '@rc-component/context'; +import { responseImmutable, useContext } from '@rc-component/context'; import classNames from 'classnames'; import * as React from 'react'; import Cell from '../Cell'; import TableContext from '../context/TableContext'; +import devRenderTimes from '../hooks/useRenderTimes'; import type { ColumnType, CustomizeComponent, @@ -19,7 +20,6 @@ export interface BodyRowProps { renderIndex: number; className?: string; style?: React.CSSProperties; - recordKey: Key; expandedKeys: Set; rowComponent: CustomizeComponent; cellComponent: CustomizeComponent; @@ -35,6 +35,10 @@ export interface BodyRowProps { function BodyRow( props: BodyRowProps, ) { + if (process.env.NODE_ENV !== 'production') { + devRenderTimes(props); + } + const { className, style, @@ -80,7 +84,24 @@ function BodyRow( ]); const [expandRended, setExpandRended] = React.useState(false); - const expanded = expandedKeys && expandedKeys.has(props.recordKey); + if (process.env.NODE_ENV !== 'production') { + devRenderTimes({ + prefixCls, + fixedInfoList, + flattenColumns, + expandableType, + expandRowByClick, + onTriggerExpand, + rowClassName, + expandedRowClassName, + indentSize, + expandIcon, + expandedRowRender, + expandIconColumnIndex, + }); + } + + const expanded = expandedKeys && expandedKeys.has(rowKey); React.useEffect(() => { if (expanded) { @@ -230,4 +251,4 @@ function BodyRow( BodyRow.displayName = 'BodyRow'; -export default BodyRow; +export default responseImmutable(BodyRow); diff --git a/src/Body/ExpandedRow.tsx b/src/Body/ExpandedRow.tsx index a3ab140ae..daa1fc15b 100644 --- a/src/Body/ExpandedRow.tsx +++ b/src/Body/ExpandedRow.tsx @@ -2,6 +2,7 @@ import { useContext } from '@rc-component/context'; import * as React from 'react'; import Cell from '../Cell'; import TableContext from '../context/TableContext'; +import devRenderTimes from '../hooks/useRenderTimes'; import type { CustomizeComponent } from '../interface'; export interface ExpandedRowProps { @@ -15,66 +16,58 @@ export interface ExpandedRowProps { isEmpty: boolean; } -function ExpandedRow({ - prefixCls, - children, - component: Component, - cellComponent, - className, - expanded, - colSpan, - isEmpty, -}: ExpandedRowProps) { +function ExpandedRow(props: ExpandedRowProps) { + if (process.env.NODE_ENV !== 'production') { + devRenderTimes(props); + } + + const { + prefixCls, + children, + component: Component, + cellComponent, + className, + expanded, + colSpan, + isEmpty, + } = props; + const { scrollbarSize, fixHeader, fixColumn, componentWidth, horizonScroll } = useContext( TableContext, ['scrollbarSize', 'fixHeader', 'fixColumn', 'componentWidth', 'horizonScroll'], ); // Cache render node - return React.useMemo(() => { - let contentNode = children; - - if (isEmpty ? horizonScroll : fixColumn) { - contentNode = ( -
- {componentWidth !== 0 && contentNode} -
- ); - } + let contentNode = children; - return ( - - - {contentNode} - - + {componentWidth !== 0 && contentNode} + ); - }, [ - children, - Component, - className, - expanded, - colSpan, - isEmpty, - scrollbarSize, - componentWidth, - fixColumn, - fixHeader, - horizonScroll, - ]); + } + + return ( + + + {contentNode} + + + ); } export default ExpandedRow; diff --git a/src/Body/index.tsx b/src/Body/index.tsx index 30606150a..489ead4a8 100644 --- a/src/Body/index.tsx +++ b/src/Body/index.tsx @@ -1,9 +1,10 @@ -import { useContext } from '@rc-component/context'; +import { responseImmutable, useContext } from '@rc-component/context'; import * as React from 'react'; import type { PerfRecord } from '../context/PerfContext'; import PerfContext from '../context/PerfContext'; import TableContext from '../context/TableContext'; import useFlattenRecords from '../hooks/useFlattenRecords'; +import devRenderTimes from '../hooks/useRenderTimes'; import type { GetComponentProps, GetRowKey, Key } from '../interface'; import { getColumnsKey } from '../utils/valueUtil'; import BodyRow from './BodyRow'; @@ -21,16 +22,22 @@ export interface BodyProps { childrenColumnName: string; } -function Body({ - data, - getRowKey, - measureColumnWidth, - expandedKeys, - onRow, - rowExpandable, - emptyNode, - childrenColumnName, -}: BodyProps) { +function Body(props: BodyProps) { + if (process.env.NODE_ENV !== 'production') { + devRenderTimes(props); + } + + const { + data, + getRowKey, + measureColumnWidth, + expandedKeys, + onRow, + rowExpandable, + emptyNode, + childrenColumnName, + } = props; + const { prefixCls, getComponent, onColumnResize, flattenColumns } = useContext(TableContext, [ 'prefixCls', 'getComponent', @@ -47,58 +54,57 @@ function Body({ }); // ====================== Render ====================== - const bodyNode = React.useMemo(() => { - const WrapperComponent = getComponent(['body', 'wrapper'], 'tbody'); - const trComponent = getComponent(['body', 'row'], 'tr'); - const tdComponent = getComponent(['body', 'cell'], 'td'); - const thComponent = getComponent(['body', 'cell'], 'th'); + const WrapperComponent = getComponent(['body', 'wrapper'], 'tbody'); + const trComponent = getComponent(['body', 'row'], 'tr'); + const tdComponent = getComponent(['body', 'cell'], 'td'); + const thComponent = getComponent(['body', 'cell'], 'th'); - let rows: React.ReactNode; - if (data.length) { - rows = flattenData.map((item, idx) => { - const { record, indent, index: renderIndex } = item; + let rows: React.ReactNode; + if (data.length) { + rows = flattenData.map((item, idx) => { + const { record, indent, index: renderIndex } = item; - const key = getRowKey(record, idx); + const key = getRowKey(record, idx); - return ( - - ); - }); - } else { - rows = ( - - {emptyNode} - + scopeCellComponent={thComponent} + expandedKeys={expandedKeys} + onRow={onRow} + getRowKey={getRowKey} + rowExpandable={rowExpandable} + childrenColumnName={childrenColumnName} + indent={indent} + /> ); - } + }); + } else { + rows = ( + + {emptyNode} + + ); + } - const columnsKey = getColumnsKey(flattenColumns); + const columnsKey = getColumnsKey(flattenColumns); - return ( + return ( + {/* Measure body column width with additional hidden col */} {measureColumnWidth && ( @@ -111,27 +117,10 @@ function Body({ {rows} - ); - }, [ - data, - prefixCls, - onRow, - measureColumnWidth, - expandedKeys, - getRowKey, - getComponent, - emptyNode, - flattenColumns, - childrenColumnName, - onColumnResize, - rowExpandable, - flattenData, - ]); - - return {bodyNode}; + + ); } -const MemoBody = React.memo(Body); -MemoBody.displayName = 'Body'; +Body.displayName = 'Body'; -export default MemoBody; +export default responseImmutable(Body); diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index 79f0196c2..7d60c7ea0 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -7,7 +7,7 @@ import ColGroup from '../ColGroup'; import TableContext from '../context/TableContext'; import type { HeaderProps } from '../Header/Header'; import devRenderTimes from '../hooks/useRenderTimes'; -import type { ColumnsType, ColumnType } from '../interface'; +import type { ColumnsType, ColumnType, Direction } from '../interface'; function useColumnWidth(colWidths: readonly number[], columCount: number) { return useMemo(() => { @@ -30,7 +30,7 @@ export interface FixedHeaderProps extends HeaderProps { maxContentScroll: boolean; colWidths: readonly number[]; columCount: number; - direction: 'ltr' | 'rtl'; + direction: Direction; fixHeader: boolean; stickyTopOffset?: number; stickyBottomOffset?: number; diff --git a/src/Table.tsx b/src/Table.tsx index f5ecdaa49..1f48333af 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -47,6 +47,7 @@ import Summary from './Footer/Summary'; import Header from './Header/Header'; import useColumns from './hooks/useColumns'; import useExpand from './hooks/useExpand'; +import useFixedInfo from './hooks/useFixedInfo'; import { useLayoutState, useTimeoutLock } from './hooks/useFrame'; import useHover from './hooks/useHover'; import useSticky from './hooks/useSticky'; @@ -56,6 +57,7 @@ import type { ColumnType, CustomizeScrollBody, DefaultRecordType, + Direction, ExpandableConfig, GetComponent, GetComponentProps, @@ -71,7 +73,6 @@ import Panel from './Panel'; import StickyScrollBar from './stickyScrollBar'; import Column from './sugar/Column'; import ColumnGroup from './sugar/ColumnGroup'; -import { getCellFixedInfo } from './utils/fixUtil'; import { getColumnsKey, validateValue } from './utils/valueUtil'; // Used for conditions cache @@ -136,7 +137,7 @@ export interface TableProps onHeaderRow?: GetComponentProps[]>; emptyText?: React.ReactNode | (() => React.ReactNode); - direction?: 'ltr' | 'rtl'; + direction?: Direction; // =================================== Internal =================================== /** @@ -747,6 +748,8 @@ function Table(tableProps: TableProps{fullTable}; } + const fixedInfoList = useFixedInfo(flattenColumns, stickyOffsets, direction); + const TableContextValue = React.useMemo( () => ({ // Table @@ -754,9 +757,7 @@ function Table(tableProps: TableProps - getCellFixedInfo(colIndex, colIndex, flattenColumns, stickyOffsets, direction), - ), + fixedInfoList, isSticky, supportSticky, @@ -795,7 +796,7 @@ function Table(tableProps: TableProps { prefixCls: string; getComponent: GetComponent; scrollbarSize: number; - direction: 'ltr' | 'rtl'; + direction: Direction; fixedInfoList: readonly FixedInfo[]; isSticky: boolean; supportSticky: boolean; diff --git a/src/hooks/useColumns.tsx b/src/hooks/useColumns.tsx index 6822aab80..457b7a8c8 100644 --- a/src/hooks/useColumns.tsx +++ b/src/hooks/useColumns.tsx @@ -10,6 +10,7 @@ import type { TriggerEventHandler, RenderExpandIcon, ColumnGroupType, + Direction, } from '../interface'; import { INTERNAL_COL_DEFINE } from '../utils/legacyUtil'; import { EXPAND_COLUMN } from '../constant'; @@ -135,7 +136,7 @@ function useColumns( expandIcon?: RenderExpandIcon; rowExpandable?: (record: RecordType) => boolean; expandIconColumnIndex?: number; - direction?: 'ltr' | 'rtl'; + direction?: Direction; expandRowByClick?: boolean; columnWidth?: number | string; fixed?: FixedType; diff --git a/src/hooks/useFixedInfo.ts b/src/hooks/useFixedInfo.ts new file mode 100644 index 000000000..d2ea2e687 --- /dev/null +++ b/src/hooks/useFixedInfo.ts @@ -0,0 +1,20 @@ +import useMemo from 'rc-util/lib/hooks/useMemo'; +import isEqual from 'rc-util/lib/isEqual'; +import type { ColumnType, Direction, StickyOffsets } from '../interface'; +import { getCellFixedInfo } from '../utils/fixUtil'; + +export default function useFixedInfo( + flattenColumns: readonly ColumnType[], + stickyOffsets: StickyOffsets, + direction: Direction, +) { + const fixedInfoList = flattenColumns.map((_, colIndex) => + getCellFixedInfo(colIndex, colIndex, flattenColumns, stickyOffsets, direction), + ); + + return useMemo( + () => fixedInfoList, + [fixedInfoList], + (prev, next) => !isEqual(prev, next), + ); +} diff --git a/src/hooks/useStickyOffsets.ts b/src/hooks/useStickyOffsets.ts index 57314281d..847cc4132 100644 --- a/src/hooks/useStickyOffsets.ts +++ b/src/hooks/useStickyOffsets.ts @@ -1,10 +1,10 @@ import { useMemo } from 'react'; -import type { StickyOffsets } from '../interface'; +import type { Direction, StickyOffsets } from '../interface'; /** * Get sticky column offset width */ -function useStickyOffsets(colWidths: number[], columnCount: number, direction: 'ltr' | 'rtl') { +function useStickyOffsets(colWidths: number[], columnCount: number, direction: Direction) { const stickyOffsets: StickyOffsets = useMemo(() => { const leftOffsets: number[] = []; const rightOffsets: number[] = []; diff --git a/src/interface.ts b/src/interface.ts index 6e8d265b0..d3c2c99a6 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -53,6 +53,8 @@ export interface RenderedCell { children?: React.ReactNode; } +export type Direction = 'ltr' | 'rtl'; + export type DataIndex = string | number | readonly (string | number)[]; export type CellEllipsisType = { showTitle?: boolean } | boolean; diff --git a/src/utils/fixUtil.ts b/src/utils/fixUtil.ts index 8128d39c7..7d37282f4 100644 --- a/src/utils/fixUtil.ts +++ b/src/utils/fixUtil.ts @@ -1,4 +1,4 @@ -import type { StickyOffsets, FixedType } from '../interface'; +import type { StickyOffsets, FixedType, Direction } from '../interface'; export interface FixedInfo { fixLeft: number | false; @@ -18,7 +18,7 @@ export function getCellFixedInfo( colEnd: number, columns: readonly { fixed?: FixedType }[], stickyOffsets: StickyOffsets, - direction: 'ltr' | 'rtl', + direction: Direction, ): FixedInfo { const startColumn = columns[colStart] || {}; const endColumn = columns[colEnd] || {}; diff --git a/tests/Table.spec.js b/tests/Table.spec.js index 49a4a4593..ab9db0853 100644 --- a/tests/Table.spec.js +++ b/tests/Table.spec.js @@ -2,6 +2,7 @@ import { mount } from 'enzyme'; import { resetWarned } from 'rc-util/lib/warning'; import React from 'react'; import Table, { INTERNAL_COL_DEFINE } from '../src'; +import BodyRow from '../src/Body/BodyRow'; import { INTERNAL_HOOKS } from '../src/Table'; describe('Table.Basic', () => { @@ -152,20 +153,20 @@ describe('Table.Basic', () => { describe('rowKey', () => { it('uses record.key', () => { const wrapper = mount(createTable()); - expect(wrapper.find('BodyRow').at(0).key()).toBe('key0'); - expect(wrapper.find('BodyRow').at(1).key()).toBe('key1'); + expect(wrapper.find(BodyRow).at(0).key()).toBe('key0'); + expect(wrapper.find(BodyRow).at(1).key()).toBe('key1'); }); it('sets by rowKey', () => { const wrapper = mount(createTable({ rowKey: 'name' })); - expect(wrapper.find('BodyRow').at(0).key()).toBe('Lucy'); - expect(wrapper.find('BodyRow').at(1).key()).toBe('Jack'); + expect(wrapper.find(BodyRow).at(0).key()).toBe('Lucy'); + expect(wrapper.find(BodyRow).at(1).key()).toBe('Jack'); }); it('sets by rowKey function', () => { const wrapper = mount(createTable({ rowKey: record => `${record.key}1` })); - expect(wrapper.find('BodyRow').at(0).key()).toBe('key01'); - expect(wrapper.find('BodyRow').at(1).key()).toBe('key11'); + expect(wrapper.find(BodyRow).at(0).key()).toBe('key01'); + expect(wrapper.find(BodyRow).at(1).key()).toBe('key11'); }); });