perf(TableBlockProvider): prevent unnecessary re-renders by splitting context

This commit is contained in:
Zeke Zhang 2024-11-03 12:55:23 +08:00
parent 063c2f7160
commit 583ab0b54b
4 changed files with 84 additions and 30 deletions

View File

@ -22,6 +22,12 @@ import { useBlockHeightProps } from './hooks';
export const TableBlockContext = createContext<any>({});
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 (
<TableBlockContext.Provider
value={{
...others,
field,
service,
resource,
params,
showIndex,
dragSort,
rowKey,
expandFlag,
childrenColumnName,
allIncludesChildren,
setExpandFlag: setExpandFlagValue,
heightProps,
}}
>
{props.children}
<TableBlockContext.Provider value={value}>
<TableBlockContextBasicValue.Provider value={basicValue}>{props.children}</TableBlockContextBasicValue.Provider>
</TableBlockContext.Provider>
);
};
@ -188,3 +221,10 @@ export const TableBlockProvider = withDynamicSchemaProps((props) => {
export const useTableBlockContext = () => {
return useContext(TableBlockContext);
};
/**
* @internal
*/
export const useTableBlockContextBasicValue = () => {
return useContext(TableBlockContextBasicValue);
};

View File

@ -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);
};

View File

@ -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<string, PopupContextStorage> = {};
@ -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,
],
);

View File

@ -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(() => {