fix: schema component options add memo

This commit is contained in:
dream2023 2024-03-06 15:17:12 +08:00
parent 55d055f801
commit 2c3a04c5c1
13 changed files with 152 additions and 108 deletions

View File

@ -29,15 +29,20 @@ export const ScopeSelect = (props) => {
}),
[],
);
const scope = useMemo(() => {
return {
onChange(value) {
props?.onChange?.(value);
},
};
}, [props?.onChange])
return (
<FormProvider form={form}>
<SchemaComponent
components={{ RolesResourcesScopesSelectedRowKeysProvider }}
scope={{
onChange(value) {
props?.onChange?.(value);
},
}}
scope={scope}
schema={scopesSchema}
/>
</FormProvider>

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { Plugin } from '../application/Plugin';
import { ActionSchemaToolbar } from '../modules/actions/ActionSchemaToolbar';
import { CollapseItemSchemaToolbar } from '../modules/blocks/filter-blocks/collapse/CollapseItemSchemaToolbar';
@ -18,9 +18,7 @@ import { BlockSchemaToolbar } from '../modules/blocks/BlockSchemaToolbar';
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
export const BlockSchemaComponentProvider: React.FC = (props) => {
return (
<SchemaComponentOptions
components={{
const components = useMemo(() => ({
TableFieldProvider,
TableBlockProvider,
TableSelectorProvider,
@ -29,8 +27,9 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
FormFieldProvider,
DetailsBlockProvider,
RecordLink,
}}
scope={{
}), [])
const scope = useMemo(() => ({
...bp,
useSourceIdFromRecord,
useSourceIdFromParentRecord,
@ -41,7 +40,12 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
useTableFieldProps,
useTableBlockProps,
useTableSelectorProps,
}}
}), [])
return (
<SchemaComponentOptions
components={components}
scope={scope}
>
{props.children}
</SchemaComponentOptions>

View File

@ -111,8 +111,9 @@ export const TableBlockProvider = (props) => {
};
}, [parsedFilter, params]);
const scope = useMemo(() => ({ treeTable }), [treeTable]);
return (
<SchemaComponentOptions scope={{ treeTable }}>
<SchemaComponentOptions scope={scope}>
<FormContext.Provider value={form}>
<BlockProvider name={props.name || 'table'} {...props} params={paramsWithFilter} runWhenParamsChanged>
<InternalTableBlockProvider {...props} childrenColumnName={childrenColumnName} params={paramsWithFilter} />

View File

@ -2,7 +2,7 @@ import { ArrayField } from '@formily/core';
import { Schema, useField, useFieldSchema } from '@formily/react';
import _ from 'lodash';
import uniq from 'lodash/uniq';
import React, { createContext, useContext, useEffect, useState } from 'react';
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { useCollectionManager_deprecated } from '../collection-manager';
import { useCollectionParentRecordData } from '../data-source/collection-record/CollectionRecordProvider';
import { isInFilterFormBlock } from '../filter-provider';
@ -153,6 +153,8 @@ export const TableSelectorProvider = (props: TableSelectorProviderProps) => {
const collection = getCollection(collectionField?.collectionName);
const primaryKey = collection?.getPrimaryKey();
const appends = useAssociationNames(props.collection);
const scope = useMemo(() => ({ treeTable }), [treeTable]);
let params = { ...props.params };
if (props.dragSort) {
params['sort'] = ['sort'];
@ -255,9 +257,8 @@ export const TableSelectorProvider = (props: TableSelectorProviderProps) => {
if (params?.filter) {
params.filter = parsedFilter;
}
return (
<SchemaComponentOptions scope={{ treeTable }}>
<SchemaComponentOptions scope={scope}>
<BlockProvider name="table-selector" {...props} params={params}>
<InternalTableSelectorProvider {...props} params={params} extraFilter={extraFilter} />
</BlockProvider>

View File

@ -1,7 +1,7 @@
import { ArrayField } from '@formily/core';
import { useField } from '@formily/react';
import { error } from '@nocobase/utils/client';
import React from 'react';
import React, { useMemo } from 'react';
import { SchemaComponentOptions } from '..';
import { useAPIClient, useRequest } from '../api-client';
@ -74,8 +74,9 @@ const useChinaRegionLoadData = () => {
};
export const ChinaRegionProvider = (props) => {
const scope = useMemo(() => ({ useChinaRegionDataSource, useChinaRegionLoadData }), []);
return (
<SchemaComponentOptions scope={{ useChinaRegionDataSource, useChinaRegionLoadData }}>
<SchemaComponentOptions scope={scope}>
{props.children}
</SchemaComponentOptions>
);

View File

@ -180,6 +180,9 @@ export const AddCollectionAction = (props) => {
};
}, [category, items]);
const components = useMemo(() => ({ ...components, ArrayTable, TemplateSummary }), []);
const scopes = useMemo(() => ({ getContainer, useCancelAction, useCreateCollection, record, ...scope }), [scope]);
return (
<RecordProvider record={record}>
<ActionContextProvider value={{ visible, setVisible }}>
@ -192,16 +195,8 @@ export const AddCollectionAction = (props) => {
</Dropdown>
<SchemaComponent
schema={schema}
components={{ ...components, ArrayTable, TemplateSummay: TemplateSummary }}
scope={{
getContainer,
useCancelAction,
createOnly: true,
useCreateCollection,
record,
showReverseFieldConfig: true,
...scope,
}}
components={components}
scope={scopes}
/>
</ActionContextProvider>
</RecordProvider>

View File

@ -199,12 +199,19 @@ const MenuEditor = (props) => {
},
);
const scope = useMemo(() => ({
useMenuProps,
onSelect,
sideMenuRef,
defaultSelectedUid
}), [])
if (loading) {
return render();
}
return (
<SchemaIdContext.Provider value={defaultSelectedUid}>
<SchemaComponent memoized scope={{ useMenuProps, onSelect, sideMenuRef, defaultSelectedUid }} schema={schema} />
<SchemaComponent memoized scope={scope} schema={schema} />
</SchemaIdContext.Provider>
);
};

View File

@ -29,6 +29,22 @@ import { useTableSelectorProps } from './InternalPicker';
import { getLabelFormatValue, useLabelUiSchema } from './util';
import { markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
const usePickActionProps = () => {
const { setVisible } = useActionContext();
const { selectedRows, options, collectionField } = useContext(RecordPickerContext);
const { field } = useAssociationFieldContext<ArrayField>();
return {
onClick() {
const selectData = unionBy(selectedRows, options, collectionField?.targetKey || 'id');
const data = field.value || [];
field.value = uniqBy(data.concat(selectData), collectionField?.targetKey || 'id');
field.onInput(field.value);
setVisible(false);
},
};
};
export const SubTable: any = observer(
(props: any) => {
const { openSize } = props;
@ -88,25 +104,19 @@ export const SubTable: any = observer(
setSelectedRows,
collectionField,
};
const usePickActionProps = () => {
const { setVisible } = useActionContext();
const { selectedRows, options, collectionField } = useContext(RecordPickerContext);
return {
onClick() {
const selectData = unionBy(selectedRows, options, collectionField?.targetKey || 'id');
const data = field.value || [];
field.value = uniqBy(data.concat(selectData), collectionField?.targetKey || 'id');
field.onInput(field.value);
setVisible(false);
},
};
};
const getFilter = () => {
const targetKey = collectionField?.targetKey || 'id';
const list = options.map((option) => option[targetKey]).filter(Boolean);
const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
return filter;
};
const scope = useMemo(() => ({
usePickActionProps,
useTableSelectorProps,
useCreateActionProps,
}), []);
return (
<div
className={css`
@ -217,11 +227,7 @@ export const SubTable: any = observer(
<FormProvider>
<TableSelectorParamsProvider params={{ filter: getFilter() }}>
<SchemaComponentOptions
scope={{
usePickActionProps,
useTableSelectorProps,
useCreateActionProps,
}}
scope={scope}
>
<RecursionField
onlyRenderProperties

View File

@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
import { ArrayField } from '@formily/core';
import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
import { List as AntdList, Col, PaginationProps } from 'antd';
import React, { useCallback, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { SortableItem } from '../../common';
import { SchemaComponentOptions } from '../../core';
import { useDesigner, useProps } from '../../hooks';
@ -96,12 +96,14 @@ const InternalGridCard = (props) => {
[run, params],
);
return (
<SchemaComponentOptions
scope={{
const scope = useMemo(() => ({
useGridCardItemProps,
useGridCardActionBarProps,
}}
}), [])
return (
<SchemaComponentOptions
scope={scope}
>
<SortableItem className={cx('nb-card-list', designerCss)}>
<AntdList

View File

@ -1,5 +1,5 @@
import { IRecursionFieldProps, ISchemaFieldProps, RecursionField, Schema } from '@formily/react';
import React, { useContext, useMemo } from 'react';
import React, { memo, useCallback, useContext, useMemo } from 'react';
import { SchemaComponentContext } from '../context';
import { SchemaComponentOptions } from './SchemaComponentOptions';
@ -30,15 +30,19 @@ const RecursionSchemaComponent = (props: ISchemaFieldProps & SchemaComponentOnCh
const { components, scope, schema, ...others } = props;
const ctx = useContext(SchemaComponentContext);
const s = useMemo(() => toSchema(schema), [schema]);
return (
<SchemaComponentContext.Provider
value={{
...ctx,
refresh: () => {
const refresh = useCallback(() => {
ctx.refresh?.();
props.onChange?.(s);
},
}}
}, [s, ctx.refresh, props.onChange]);
const contextValue = useMemo(() => ({
...ctx,
refresh
}), [ctx, refresh]);
return (
<SchemaComponentContext.Provider
value={contextValue}
>
<SchemaComponentOptions inherit components={components} scope={scope}>
<RecursionField {...others} schema={s} />
@ -53,7 +57,7 @@ const MemoizedSchemaComponent = (props: ISchemaFieldProps & SchemaComponentOnCha
return <RecursionSchemaComponent {...others} schema={s} />;
};
export const SchemaComponent = (
export const SchemaComponent = memo((
props: (ISchemaFieldProps | IRecursionFieldProps) & { memoized?: boolean } & SchemaComponentOnChange,
) => {
const { memoized, ...others } = props;
@ -61,4 +65,6 @@ export const SchemaComponent = (
return <MemoizedSchemaComponent {...others} />;
}
return <RecursionSchemaComponent {...others} />;
};
});
SchemaComponent.displayName = 'SchemaComponent';

View File

@ -1,7 +1,7 @@
import { createForm } from '@formily/core';
import { FormProvider, Schema } from '@formily/react';
import { uid } from '@formily/shared';
import React, { useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaComponentContext } from '../context';
import { ISchemaComponentProvider } from '../types';
@ -53,27 +53,35 @@ export const SchemaComponentProvider: React.FC<ISchemaComponentProvider> = (prop
}, [props.scope, t]);
const [active, setActive] = useState(designable);
const schemaComponentContextValue = useMemo(
() => ({
scope,
components,
reset: () => setFormId(uid()),
refresh: () => {
const refresh = useCallback(() => {
setUid(uid());
},
designable: typeof designable === 'boolean' ? designable : active,
setDesignable: (value) => {
}, []);
const reset = useCallback(() => {
setFormId(uid());
}, []);
const setDesignable = useCallback((value) => {
if (typeof designable !== 'boolean') {
setActive(value);
}
onDesignableChange?.(value);
},
}),
[uidValue, scope, components, designable, active],
);
}, [designable, onDesignableChange]);
const contextValue = useMemo(() => {
return {
scope,
components,
reset,
refresh,
designable: typeof designable === 'boolean' ? designable : active,
setDesignable,
};
// uidValue 虽然没用到,但是这里必须加上,为了让整个页面能够渲染
}, [uidValue, scope, components, reset, refresh, designable, active, setDesignable])
return (
<SchemaComponentContext.Provider value={schemaComponentContextValue}>
<SchemaComponentContext.Provider value={contextValue}>
<FormProvider form={form}>
<SchemaComponentOptions inherit scope={scope} components={components}>
{children}

View File

@ -2,7 +2,7 @@ import { ISchema, useForm } from '@formily/react';
import { uid } from '@formily/shared';
import { Card, message } from 'antd';
import cloneDeep from 'lodash/cloneDeep';
import React from 'react';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useSystemSettings } from '.';
import { i18n, useAPIClient, useRequest } from '..';
@ -164,10 +164,11 @@ const schema: ISchema = {
};
export const SystemSettingsPane = () => {
const scope = useMemo(() => ({ useSaveSystemSettingsValues, useSystemSettingsValues, useCloseAction }), []);
return (
<Card bordered={false}>
<SchemaComponent
scope={{ useSaveSystemSettingsValues, useSystemSettingsValues, useCloseAction }}
scope={scope}
schema={schema}
/>
</Card>

View File

@ -1,5 +1,5 @@
import { SchemaComponentOptions } from '@nocobase/client';
import React, { useState } from 'react';
import React, { memo, useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import { ImportActionInitializer, ImportDesigner } from '.';
import { ImportContext } from './context';
@ -9,23 +9,30 @@ import { useShared } from './useShared';
export const ImportPluginProvider = (props: any) => {
const { uploadValidator, beforeUploadHandler, validateUpload } = useShared();
return (
<SchemaComponentOptions
components={{ ImportActionInitializer, ImportDesigner }}
scope={{
const scope = useMemo(() => ({
uploadValidator,
validateUpload,
beforeUploadHandler,
useDownloadXlsxTemplateAction,
useImportStartAction,
}}
}), [uploadValidator, beforeUploadHandler, validateUpload])
const components = useMemo(() => ({
ImportActionInitializer,
ImportDesigner,
}), []);
return (
<SchemaComponentOptions
components={components}
scope={scope}
>
<ImportContextProvider>{props.children}</ImportContextProvider>
</SchemaComponentOptions>
);
};
export const ImportContextProvider = (props: any) => {
export const ImportContextProvider = memo((props: any) => {
const [importModalVisible, setImportModalVisible] = useState(false);
const [importStatus, setImportStatus] = useState<number>(ImportStatus.IMPORTING);
const [importResult, setImportResult] = useState<{
@ -47,4 +54,4 @@ export const ImportContextProvider = (props: any) => {
{props.children}
</ImportContext.Provider>
);
};
});