From 1145721d48e59d3258f6215649ce87ef55e332b6 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Sat, 26 Oct 2024 10:42:29 +0800 Subject: [PATCH] perf(BlockRequestProvider): prevent multiple re-renders of descendant components --- .../CollectionRecordProvider.tsx | 59 +++++++++---------- .../data-block/DataBlockRequestProvider.tsx | 36 +++++++++-- 2 files changed, 61 insertions(+), 34 deletions(-) diff --git a/packages/core/client/src/data-source/collection-record/CollectionRecordProvider.tsx b/packages/core/client/src/data-source/collection-record/CollectionRecordProvider.tsx index 3eee58214d..2b61201068 100644 --- a/packages/core/client/src/data-source/collection-record/CollectionRecordProvider.tsx +++ b/packages/core/client/src/data-source/collection-record/CollectionRecordProvider.tsx @@ -20,38 +20,37 @@ export interface CollectionRecordProviderProps | DataType; } -export const CollectionRecordProvider: FC = ({ - isNew, - record, - parentRecord, - children, -}) => { - const parentRecordValue = useMemo(() => { - if (parentRecord) { - if (parentRecord instanceof CollectionRecord) return parentRecord; - return new CollectionRecord({ data: parentRecord }); - } - if (record instanceof CollectionRecord) return record.parentRecord; - }, [parentRecord, record]); - - const currentRecordValue = useMemo(() => { - let res: CollectionRecord; - if (record) { - if (record instanceof CollectionRecord) { - res = record; - res.isNew = record.isNew || isNew; - } else { - res = new CollectionRecord({ data: record, isNew }); +export const CollectionRecordProvider: FC = React.memo( + ({ isNew, record, parentRecord, children }) => { + const parentRecordValue = useMemo(() => { + if (parentRecord) { + if (parentRecord instanceof CollectionRecord) return parentRecord; + return new CollectionRecord({ data: parentRecord }); } - } else { - res = new CollectionRecord({ isNew }); - } - res.setParentRecord(parentRecordValue); - return res; - }, [record, parentRecordValue, isNew]); + if (record instanceof CollectionRecord) return record.parentRecord; + }, [parentRecord, record]); - return {children}; -}; + const currentRecordValue = useMemo(() => { + let res: CollectionRecord; + if (record) { + if (record instanceof CollectionRecord) { + res = record; + res.isNew = record.isNew || isNew; + } else { + res = new CollectionRecord({ data: record, isNew }); + } + } else { + res = new CollectionRecord({ isNew }); + } + res.setParentRecord(parentRecordValue); + return res; + }, [record, parentRecordValue, isNew]); + + return {children}; + }, +); + +CollectionRecordProvider.displayName = 'CollectionRecordProvider'; export function useCollectionRecord(): CollectionRecord { const context = useContext>(CollectionRecordContext); 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 8b68467865..6e458bf813 100644 --- a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx +++ b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx @@ -25,6 +25,8 @@ function useCurrentRequest(options: Omit) { const dataLoadingMode = useDataLoadingMode(); const resource = useDataBlockResource(); const { action, params = {}, record, requestService, requestOptions } = options; + const JSONParams = JSON.stringify(params); + const JSONRecord = JSON.stringify(record); const service = useMemo(() => { return ( @@ -47,13 +49,13 @@ function useCurrentRequest(options: Omit) { return resource[action]?.({ ...paramsValue, ...customParams }).then((res) => res.data); }) ); - }, [resource, action, JSON.stringify(params), JSON.stringify(record), requestService]); + }, [resource, action, JSONParams, JSONRecord, requestService]); const request = useRequest(service, { ...requestOptions, manual: dataLoadingMode === 'manual', ready: !!action, - refreshDeps: [action, JSON.stringify(params), JSON.stringify(record), resource], + refreshDeps: [action, JSONParams, JSONRecord, resource], }); return request; @@ -100,6 +102,23 @@ function useParentRequest(options: Omit) { ); } +const EMPTY_REQUEST: UseRequestResult<{ data: any }> = Object.freeze({ + loading: true, + data: { data: undefined }, + error: undefined, + params: [], + run: _.noop, + runAsync: () => Promise.resolve({ data: undefined }), + refresh: _.noop, + refreshAsync: () => Promise.resolve({ data: undefined }), + mutate: _.noop, + cancel: _.noop, + state: { + data: { data: undefined }, + }, + setState: _.noop, +}); + export const BlockRequestProvider: FC = ({ children }) => { const props = useDataBlockProps(); const { @@ -115,7 +134,10 @@ export const BlockRequestProvider: FC = ({ children }) => { requestService, } = props; - const currentRequest = useCurrentRequest<{ data: any }>({ + let currentRequest = EMPTY_REQUEST, + parentRequest = EMPTY_REQUEST; + + const _currentRequest = useCurrentRequest<{ data: any }>({ action, sourceId, record, @@ -129,12 +151,18 @@ export const BlockRequestProvider: FC = ({ children }) => { }, }); - const parentRequest = useParentRequest<{ data: any }>({ + const _parentRequest = useParentRequest<{ data: any }>({ sourceId, association, parentRecord, }); + // This is to prevent multiple re-renders of descendant components + if (!_currentRequest.loading && !_parentRequest.loading) { + currentRequest = _currentRequest; + parentRequest = _parentRequest; + } + const memoizedParentRecord = useMemo(() => { return ( parentRequest.data?.data &&