mirror of
https://github.com/nocobase/nocobase
synced 2024-11-16 04:40:10 +00:00
perf(useTableBlockProps): avoid unnecessary re-renders
This commit is contained in:
parent
bca6d965ee
commit
8106424180
@ -25,6 +25,10 @@ TableBlockContext.displayName = 'TableBlockContext';
|
|||||||
const TableBlockContextBasicValue = createContext<{
|
const TableBlockContextBasicValue = createContext<{
|
||||||
field: any;
|
field: any;
|
||||||
rowKey: string;
|
rowKey: string;
|
||||||
|
dragSortBy?: string;
|
||||||
|
childrenColumnName?: string;
|
||||||
|
showIndex?: boolean;
|
||||||
|
dragSort?: boolean;
|
||||||
}>(null);
|
}>(null);
|
||||||
TableBlockContextBasicValue.displayName = 'TableBlockContextBasicValue';
|
TableBlockContextBasicValue.displayName = 'TableBlockContextBasicValue';
|
||||||
|
|
||||||
@ -56,6 +60,7 @@ interface Props {
|
|||||||
collection?: string;
|
collection?: string;
|
||||||
children?: any;
|
children?: any;
|
||||||
expandFlag?: boolean;
|
expandFlag?: boolean;
|
||||||
|
dragSortBy?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InternalTableBlockProvider = (props: Props) => {
|
const InternalTableBlockProvider = (props: Props) => {
|
||||||
@ -100,8 +105,12 @@ const InternalTableBlockProvider = (props: Props) => {
|
|||||||
() => ({
|
() => ({
|
||||||
field,
|
field,
|
||||||
rowKey,
|
rowKey,
|
||||||
|
childrenColumnName,
|
||||||
|
showIndex,
|
||||||
|
dragSort,
|
||||||
|
dragSortBy: props.dragSortBy,
|
||||||
}),
|
}),
|
||||||
[field, rowKey],
|
[field, rowKey, childrenColumnName, showIndex, dragSort, props.dragSortBy],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Keep the original for compatibility
|
// Keep the original for compatibility
|
||||||
|
@ -11,8 +11,10 @@ import { ArrayField } from '@formily/core';
|
|||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
import { useTableBlockContext } from '../../../../../block-provider/TableBlockProvider';
|
import { useTableBlockContextBasicValue } from '../../../../../block-provider/TableBlockProvider';
|
||||||
import { findFilterTargets } from '../../../../../block-provider/hooks';
|
import { findFilterTargets } from '../../../../../block-provider/hooks';
|
||||||
|
import { useDataBlockRequest } from '../../../../../data-source/data-block/DataBlockRequestProvider';
|
||||||
|
import { useDataBlockResource } from '../../../../../data-source/data-block/DataBlockResourceProvider';
|
||||||
import { DataBlock, useFilterBlock } from '../../../../../filter-provider/FilterProvider';
|
import { DataBlock, useFilterBlock } from '../../../../../filter-provider/FilterProvider';
|
||||||
import { mergeFilter } from '../../../../../filter-provider/utils';
|
import { mergeFilter } from '../../../../../filter-provider/utils';
|
||||||
import { removeNullCondition } from '../../../../../schema-component';
|
import { removeNullCondition } from '../../../../../schema-component';
|
||||||
@ -20,13 +22,15 @@ import { removeNullCondition } from '../../../../../schema-component';
|
|||||||
export const useTableBlockProps = () => {
|
export const useTableBlockProps = () => {
|
||||||
const field = useField<ArrayField>();
|
const field = useField<ArrayField>();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const ctx = useTableBlockContext();
|
const resource = useDataBlockResource();
|
||||||
|
const service = useDataBlockRequest() as any;
|
||||||
const { getDataBlocks } = useFilterBlock();
|
const { getDataBlocks } = useFilterBlock();
|
||||||
const isLoading = ctx?.service?.loading;
|
const isLoading = service?.loading;
|
||||||
|
const tableBlockContextBasicValue = useTableBlockContextBasicValue();
|
||||||
|
|
||||||
const ctxRef = useRef(null);
|
const ctxRef = useRef(null);
|
||||||
ctxRef.current = ctx;
|
ctxRef.current = { service, resource };
|
||||||
const meta = ctx?.service?.data?.meta || {};
|
const meta = service?.data?.meta || {};
|
||||||
const pagination = useMemo(
|
const pagination = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
pageSize: meta?.pageSize,
|
pageSize: meta?.pageSize,
|
||||||
@ -38,9 +42,9 @@ export const useTableBlockProps = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading) {
|
if (!isLoading) {
|
||||||
const serviceResponse = ctx?.service?.data;
|
const serviceResponse = service?.data;
|
||||||
const data = serviceResponse?.data || [];
|
const data = serviceResponse?.data || [];
|
||||||
const selectedRowKeys = ctx?.field?.data?.selectedRowKeys;
|
const selectedRowKeys = tableBlockContextBasicValue.field?.data?.selectedRowKeys;
|
||||||
|
|
||||||
if (!isEqual(field.value, data)) {
|
if (!isEqual(field.value, data)) {
|
||||||
field.value = data;
|
field.value = data;
|
||||||
@ -52,37 +56,43 @@ export const useTableBlockProps = () => {
|
|||||||
field.data.selectedRowKeys = selectedRowKeys;
|
field.data.selectedRowKeys = selectedRowKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
field.componentProps.pagination = pagination;
|
// 大概率是无用代码,且可能会影响性能
|
||||||
|
// field.componentProps.pagination = pagination;
|
||||||
}
|
}
|
||||||
}, [field, ctx?.service?.data, isLoading, ctx?.field?.data?.selectedRowKeys, pagination]);
|
}, [field, service?.data, isLoading, tableBlockContextBasicValue.field?.data?.selectedRowKeys]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultDataSource: ctx?.service?.data?.data || [],
|
defaultDataSource: service?.data?.data || [],
|
||||||
bordered: ctx.bordered,
|
childrenColumnName: tableBlockContextBasicValue.childrenColumnName,
|
||||||
childrenColumnName: ctx.childrenColumnName,
|
loading: service?.loading,
|
||||||
loading: ctx?.service?.loading,
|
showIndex: tableBlockContextBasicValue.showIndex,
|
||||||
showIndex: ctx.showIndex,
|
dragSort: tableBlockContextBasicValue.dragSort && tableBlockContextBasicValue.dragSortBy,
|
||||||
dragSort: ctx.dragSort && ctx.dragSortBy,
|
rowKey: tableBlockContextBasicValue.rowKey || fieldSchema?.['x-component-props']?.rowKey || 'id',
|
||||||
rowKey: ctx.rowKey || fieldSchema?.['x-component-props']?.rowKey || 'id',
|
|
||||||
pagination: fieldSchema?.['x-component-props']?.pagination === false ? false : pagination,
|
pagination: fieldSchema?.['x-component-props']?.pagination === false ? false : pagination,
|
||||||
onRowSelectionChange: useCallback((selectedRowKeys, selectedRowData) => {
|
onRowSelectionChange: useCallback((selectedRowKeys, selectedRowData) => {
|
||||||
ctx.field.data = ctx?.field?.data || {};
|
if (tableBlockContextBasicValue) {
|
||||||
ctx.field.data.selectedRowKeys = selectedRowKeys;
|
tableBlockContextBasicValue.field.data = tableBlockContextBasicValue.field?.data || {};
|
||||||
ctx.field.data.selectedRowData = selectedRowData;
|
tableBlockContextBasicValue.field.data.selectedRowKeys = selectedRowKeys;
|
||||||
ctx?.field?.onRowSelect?.(selectedRowKeys);
|
tableBlockContextBasicValue.field.data.selectedRowData = selectedRowData;
|
||||||
|
tableBlockContextBasicValue.field?.onRowSelect?.(selectedRowKeys);
|
||||||
|
}
|
||||||
}, []),
|
}, []),
|
||||||
onRowDragEnd: useCallback(
|
onRowDragEnd: useCallback(
|
||||||
async ({ from, to }) => {
|
async ({ from, to }) => {
|
||||||
await ctx.resource.move({
|
await ctxRef.current.resource.move({
|
||||||
sourceId: from[ctx.rowKey || 'id'],
|
sourceId: from[tableBlockContextBasicValue.rowKey || 'id'],
|
||||||
targetId: to[ctx.rowKey || 'id'],
|
targetId: to[tableBlockContextBasicValue.rowKey || 'id'],
|
||||||
sortField: ctx.dragSort && ctx.dragSortBy,
|
sortField: tableBlockContextBasicValue.dragSort && tableBlockContextBasicValue.dragSortBy,
|
||||||
});
|
});
|
||||||
ctx.service.refresh();
|
ctxRef.current.service.refresh();
|
||||||
// ctx.resource
|
// ctx.resource
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
},
|
},
|
||||||
[ctx.rowKey, ctx.dragSort, ctx.dragSortBy],
|
[
|
||||||
|
tableBlockContextBasicValue.rowKey,
|
||||||
|
tableBlockContextBasicValue.dragSort,
|
||||||
|
tableBlockContextBasicValue.dragSortBy,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
onChange: useCallback(
|
onChange: useCallback(
|
||||||
({ current, pageSize }, filters, sorter) => {
|
({ current, pageSize }, filters, sorter) => {
|
||||||
@ -91,7 +101,7 @@ export const useTableBlockProps = () => {
|
|||||||
? sorter.order === `ascend`
|
? sorter.order === `ascend`
|
||||||
? [sorter.field]
|
? [sorter.field]
|
||||||
: [`-${sorter.field}`]
|
: [`-${sorter.field}`]
|
||||||
: globalSort || ctxRef.current.dragSortBy;
|
: globalSort || tableBlockContextBasicValue.dragSortBy;
|
||||||
const currentPageSize = pageSize || fieldSchema.parent?.['x-decorator-props']?.['params']?.pageSize;
|
const currentPageSize = pageSize || fieldSchema.parent?.['x-decorator-props']?.['params']?.pageSize;
|
||||||
const args = { ...ctxRef.current?.service?.params?.[0], page: current || 1, pageSize: currentPageSize };
|
const args = { ...ctxRef.current?.service?.params?.[0], page: current || 1, pageSize: currentPageSize };
|
||||||
if (sort) {
|
if (sort) {
|
||||||
@ -122,14 +132,14 @@ export const useTableBlockProps = () => {
|
|||||||
|
|
||||||
const isForeignKey = block.foreignKeyFields?.some((field) => field.name === target.field);
|
const isForeignKey = block.foreignKeyFields?.some((field) => field.name === target.field);
|
||||||
const sourceKey = getSourceKey(currentBlock, target.field);
|
const sourceKey = getSourceKey(currentBlock, target.field);
|
||||||
const recordKey = isForeignKey ? sourceKey : ctx.rowKey;
|
const recordKey = isForeignKey ? sourceKey : tableBlockContextBasicValue.rowKey;
|
||||||
const value = [record[recordKey]];
|
const value = [record[recordKey]];
|
||||||
|
|
||||||
const param = block.service.params?.[0] || {};
|
const param = block.service.params?.[0] || {};
|
||||||
// 保留原有的 filter
|
// 保留原有的 filter
|
||||||
const storedFilter = block.service.params?.[1]?.filters || {};
|
const storedFilter = block.service.params?.[1]?.filters || {};
|
||||||
|
|
||||||
if (selectedRow.includes(record[ctx.rowKey])) {
|
if (selectedRow.includes(record[tableBlockContextBasicValue.rowKey])) {
|
||||||
if (block.dataLoadingMode === 'manual') {
|
if (block.dataLoadingMode === 'manual') {
|
||||||
return block.clearData();
|
return block.clearData();
|
||||||
}
|
}
|
||||||
@ -138,7 +148,7 @@ export const useTableBlockProps = () => {
|
|||||||
storedFilter[uid] = {
|
storedFilter[uid] = {
|
||||||
$and: [
|
$and: [
|
||||||
{
|
{
|
||||||
[target.field || ctx.rowKey]: {
|
[target.field || tableBlockContextBasicValue.rowKey]: {
|
||||||
[target.field ? '$in' : '$eq']: value,
|
[target.field ? '$in' : '$eq']: value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -162,12 +172,16 @@ export const useTableBlockProps = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 更新表格的选中状态
|
// 更新表格的选中状态
|
||||||
setSelectedRow((prev) => (prev?.includes(record[ctx.rowKey]) ? [] : [record[ctx.rowKey]]));
|
setSelectedRow((prev) =>
|
||||||
|
prev?.includes(record[tableBlockContextBasicValue.rowKey])
|
||||||
|
? []
|
||||||
|
: [record[tableBlockContextBasicValue.rowKey]],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[ctx.rowKey, fieldSchema, getDataBlocks],
|
[tableBlockContextBasicValue.rowKey, fieldSchema, getDataBlocks],
|
||||||
),
|
),
|
||||||
onExpand: useCallback((expanded, record) => {
|
onExpand: useCallback((expanded, record) => {
|
||||||
ctx?.field.onExpandClick?.(expanded, record);
|
tableBlockContextBasicValue.field.onExpandClick?.(expanded, record);
|
||||||
}, []),
|
}, []),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -433,28 +433,40 @@ const rowSelectCheckboxCheckedClassHover = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const HeaderWrapperComponent = (props) => {
|
const HeaderWrapperComponent = React.memo((props) => {
|
||||||
return (
|
return (
|
||||||
<DndContext>
|
<DndContext>
|
||||||
<thead {...props} />
|
<thead {...props} />
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
const HeaderCellComponent = (props) => {
|
HeaderWrapperComponent.displayName = 'HeaderWrapperComponent';
|
||||||
|
|
||||||
|
const HeaderCellComponent = React.memo((props: { className: string }) => {
|
||||||
return <th {...props} className={cls(props.className, headerClass)} />;
|
return <th {...props} className={cls(props.className, headerClass)} />;
|
||||||
};
|
});
|
||||||
|
|
||||||
const BodyRowComponent = (props: {
|
HeaderCellComponent.displayName = 'HeaderCellComponent';
|
||||||
rowIndex: number;
|
|
||||||
onClick: (e: any) => void;
|
const BodyRowComponent = React.memo(
|
||||||
style: React.CSSProperties;
|
(props: { rowIndex: number; onClick: (e: any) => void; style: React.CSSProperties; className: string }) => {
|
||||||
className: string;
|
|
||||||
}) => {
|
|
||||||
return <SortableRow {...props} />;
|
return <SortableRow {...props} />;
|
||||||
};
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const BodyCellComponent = (props) => {
|
BodyRowComponent.displayName = 'BodyRowComponent';
|
||||||
|
|
||||||
|
const BodyCellComponent = React.memo(
|
||||||
|
(props: {
|
||||||
|
className: string;
|
||||||
|
style: React.CSSProperties;
|
||||||
|
children: React.ReactNode;
|
||||||
|
record: any;
|
||||||
|
schema: any;
|
||||||
|
rowIndex: number;
|
||||||
|
isSubTable: boolean;
|
||||||
|
}) => {
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
const inView = useContext(InViewContext);
|
const inView = useContext(InViewContext);
|
||||||
const isIndex = props.className?.includes('selection-column');
|
const isIndex = props.className?.includes('selection-column');
|
||||||
@ -473,7 +485,10 @@ const BodyCellComponent = (props) => {
|
|||||||
{isSubTable || inView || isIndex ? props.children : <div style={skeletonStyle} />}
|
{isSubTable || inView || isIndex ? props.children : <div style={skeletonStyle} />}
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
BodyCellComponent.displayName = 'BodyCellComponent';
|
||||||
|
|
||||||
interface TableProps {
|
interface TableProps {
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
|
Loading…
Reference in New Issue
Block a user