perf(BlockRequestProvider): prevent multiple re-renders of descendant components

This commit is contained in:
Zeke Zhang 2024-10-26 10:42:29 +08:00
parent 261c2604c5
commit 1145721d48
2 changed files with 61 additions and 34 deletions

View File

@ -20,38 +20,37 @@ export interface CollectionRecordProviderProps<DataType = {}, ParentDataType = {
parentRecord?: CollectionRecord<ParentDataType> | DataType;
}
export const CollectionRecordProvider: FC<CollectionRecordProviderProps> = ({
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<CollectionRecordProviderProps> = 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 <CollectionRecordContext.Provider value={currentRecordValue}>{children}</CollectionRecordContext.Provider>;
};
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 <CollectionRecordContext.Provider value={currentRecordValue}>{children}</CollectionRecordContext.Provider>;
},
);
CollectionRecordProvider.displayName = 'CollectionRecordProvider';
export function useCollectionRecord<DataType = {}, ParentDataType = {}>(): CollectionRecord<DataType, ParentDataType> {
const context = useContext<CollectionRecord<DataType, ParentDataType>>(CollectionRecordContext);

View File

@ -25,6 +25,8 @@ function useCurrentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
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<T>(options: Omit<AllDataBlockProps, 'type'>) {
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<T>(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<T>(options: Omit<AllDataBlockProps, 'type'>) {
);
}
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 &&