From 583ab0b54b0ee3fa87aa67b715f7dfd4c4e5dd15 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Sun, 3 Nov 2024 12:55:23 +0800 Subject: [PATCH] perf(TableBlockProvider): prevent unnecessary re-renders by splitting context --- .../src/block-provider/TableBlockProvider.tsx | 78 ++++++++++++++----- .../data-block/DataBlockRequestProvider.tsx | 7 ++ .../antd/page/pagePopupUtils.tsx | 13 ++-- .../src/variables/hooks/useContextVariable.ts | 16 ++-- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/packages/core/client/src/block-provider/TableBlockProvider.tsx b/packages/core/client/src/block-provider/TableBlockProvider.tsx index 180cd3a886..7cd81ba510 100644 --- a/packages/core/client/src/block-provider/TableBlockProvider.tsx +++ b/packages/core/client/src/block-provider/TableBlockProvider.tsx @@ -22,6 +22,12 @@ import { useBlockHeightProps } from './hooks'; export const TableBlockContext = createContext({}); TableBlockContext.displayName = 'TableBlockContext'; +const TableBlockContextBasicValue = createContext<{ + field: any; + rowKey: string; +}>(null); +TableBlockContextBasicValue.displayName = 'TableBlockContextBasicValue'; + /** * @internal */ @@ -61,7 +67,7 @@ const InternalTableBlockProvider = (props: Props) => { childrenColumnName, expandFlag: propsExpandFlag = false, fieldNames, - ...others + collection, } = props; const field: any = useField(); const { resource, service } = useBlockRequestContext(); @@ -89,25 +95,52 @@ const InternalTableBlockProvider = (props: Props) => { [expandFlag], ); + // Split from value to prevent unnecessary re-renders + const basicValue = useMemo( + () => ({ + field, + rowKey, + }), + [field, rowKey], + ); + + // Keep the original for compatibility + const value = useMemo( + () => ({ + collection, + field, + service, + resource, + params, + showIndex, + dragSort, + rowKey, + expandFlag, + childrenColumnName, + allIncludesChildren, + setExpandFlag: setExpandFlagValue, + heightProps, + }), + [ + allIncludesChildren, + childrenColumnName, + collection, + dragSort, + expandFlag, + field, + heightProps, + params, + resource, + rowKey, + service, + setExpandFlagValue, + showIndex, + ], + ); + return ( - - {props.children} + + {props.children} ); }; @@ -188,3 +221,10 @@ export const TableBlockProvider = withDynamicSchemaProps((props) => { export const useTableBlockContext = () => { return useContext(TableBlockContext); }; + +/** + * @internal + */ +export const useTableBlockContextBasicValue = () => { + return useContext(TableBlockContextBasicValue); +}; diff --git a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx index 1f9e5b731b..622b061898 100644 --- a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx +++ b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx @@ -203,3 +203,10 @@ export const useDataBlockRequestGetter = () => { [contextRef], ); }; + +/** + * When only data is needed, it's recommended to use this hook to avoid unnecessary re-renders + */ +export const useDataBlockRequestData = () => { + return useContext(BlockRequestDataContext); +}; diff --git a/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx b/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx index bd022d4550..b335626e17 100644 --- a/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx +++ b/packages/core/client/src/schema-component/antd/page/pagePopupUtils.tsx @@ -11,7 +11,7 @@ import { ISchema, useFieldSchema } from '@formily/react'; import _ from 'lodash'; import { useCallback, useContext } from 'react'; import { useLocationNoUpdate, useNavigateNoUpdate } from '../../../application'; -import { useTableBlockContext } from '../../../block-provider/TableBlockProvider'; +import { useTableBlockContextBasicValue } from '../../../block-provider/TableBlockProvider'; import { CollectionRecord, useAssociationName, @@ -19,6 +19,7 @@ import { useCollectionManager, useCollectionParentRecord, useCollectionRecord, + useDataBlockRequestData, useDataBlockRequestGetter, useDataSourceKey, } from '../../../data-source'; @@ -50,7 +51,7 @@ export interface PopupContextStorage extends PopupContext { service?: any; sourceId?: string; /** Specifically prepared for the 'Table selected records' variable */ - tableBlockContext?: { field: any; service: any; rowKey: any; collection: string }; + tableBlockContext?: { field: any; blockData: any; rowKey: any; collection: string }; } const popupsContextStorage: Record = {}; @@ -157,7 +158,8 @@ export const usePopupUtils = ( (_parentRecordData || parentRecord?.data)?.[cm.getSourceKeyByAssociation(association)], [parentRecord, association], ); - const tableBlockContext = useTableBlockContext(); + const blockData = useDataBlockRequestData(); + const tableBlockContextBasicValue = useTableBlockContextBasicValue() || ({} as any); const setVisibleFromAction = options.setVisible || _setVisibleFromAction; @@ -249,7 +251,7 @@ export const usePopupUtils = ( collection: collection?.name, association, sourceId, - tableBlockContext, + tableBlockContext: { ...tableBlockContextBasicValue, collection: collection?.name, blockData }, }); updatePopupContext(getNewPopupContext(), customActionSchema); @@ -271,7 +273,8 @@ export const usePopupUtils = ( isPopupVisibleControlledByURL, getSourceId, getNewPopupContext, - tableBlockContext, + blockData, + tableBlockContextBasicValue, ], ); diff --git a/packages/core/client/src/variables/hooks/useContextVariable.ts b/packages/core/client/src/variables/hooks/useContextVariable.ts index 74a71a58c6..aea4dd176b 100644 --- a/packages/core/client/src/variables/hooks/useContextVariable.ts +++ b/packages/core/client/src/variables/hooks/useContextVariable.ts @@ -8,7 +8,9 @@ */ import { useMemo } from 'react'; -import { useTableBlockContext } from '../../block-provider/TableBlockProvider'; +import { useTableBlockContextBasicValue } from '../../block-provider/TableBlockProvider'; +import { useDataBlockRequestData } from '../../data-source'; +import { useCollection } from '../../data-source/collection/CollectionProvider'; import { useCurrentPopupContext } from '../../schema-component/antd/page/PagePopups'; import { getStoredPopupContext } from '../../schema-component/antd/page/pagePopupUtils'; import { usePopupSettings } from '../../schema-component/antd/page/PopupSettingsProvider'; @@ -19,19 +21,21 @@ const useContextVariable = (): VariableOption => { const { isPopupVisibleControlledByURL } = usePopupSettings(); const { params } = useCurrentPopupContext(); - const _tableBlockContext = useTableBlockContext(); + const collection = useCollection(); + const _blockData = useDataBlockRequestData(); + const tableBlockContextBasicValue = useTableBlockContextBasicValue() || {}; if (isPopupVisibleControlledByURL()) { tableBlockContext = getStoredPopupContext(params?.popupuid)?.tableBlockContext; } else { - tableBlockContext = _tableBlockContext; + tableBlockContext = { ...tableBlockContextBasicValue, collection, blockData: _blockData }; } - const { field, service, rowKey, collection: collectionName } = tableBlockContext || {}; + const { field, blockData, rowKey, collection: collectionName } = tableBlockContext || {}; const contextData = useMemo( - () => service?.data?.data?.filter((v) => (field?.data?.selectedRowKeys || [])?.includes(v[rowKey])), - [field?.data?.selectedRowKeys, rowKey, service?.data?.data], + () => blockData?.data?.filter((v) => (field?.data?.selectedRowKeys || [])?.includes(v[rowKey])), + [field?.data?.selectedRowKeys, rowKey, blockData], ); return useMemo(() => {