From f803105e69a9ab6cc85fb2b889461133ca1263cf Mon Sep 17 00:00:00 2001 From: YANG QIA <2013xile@gmail.com> Date: Thu, 4 Jan 2024 19:21:54 +0800 Subject: [PATCH] perf(bi): optimize performance of chart filter block (#3316) * perf: add useMemo * fix: bug * fix: bug * chore: remove memo --- .../antd/cascader/Cascader.tsx | 4 +- .../src/client/chart/antd/table.ts | 6 +- .../src/client/configure/ChartConfigure.tsx | 9 ++- .../src/client/configure/schemas/configure.ts | 2 +- .../client/filter/FilterItemInitializers.tsx | 29 +++---- .../src/client/hooks/filter.ts | 38 ++++----- .../src/client/hooks/query.ts | 78 ++++++++++--------- 7 files changed, 89 insertions(+), 77 deletions(-) diff --git a/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx b/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx index f1e844931..f5dccc375 100644 --- a/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx +++ b/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx @@ -54,12 +54,12 @@ export const Cascader = connect( {labels.map((label, index) => { if (selectedOptions[index]) { - return {label}; + return {label}; } const item = toArr(value) .filter(Boolean) .find((item) => item[fieldNames.value] === label); - return {item?.[fieldNames.label] || label}; + return {item?.[fieldNames.label] || label}; })} ); diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/chart/antd/table.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/client/chart/antd/table.ts index 18c62650b..6248bd6e9 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/chart/antd/table.ts +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/chart/antd/table.ts @@ -15,8 +15,7 @@ export class Table extends AntdChart { key: item, })) : []; - const rowKey = columns[0]?.dataIndex; - const dataSource = data.map((item: any) => { + const dataSource = data.map((item: any, index: number) => { Object.keys(item).map((key: string) => { const props = fieldProps[key]; if (props?.interface === 'percent') { @@ -30,6 +29,7 @@ export class Table extends AntdChart { item[key] = props.transformer(item[key]); } }); + item._key = index; return item; }); const pageSize = advanced?.pagination?.pageSize || 10; @@ -47,7 +47,7 @@ export class Table extends AntdChart { scroll: { x: 'max-content', }, - rowKey, + rowKey: '_key', ...general, ...advanced, }; diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/ChartConfigure.tsx b/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/ChartConfigure.tsx index 39ece3d8c..842e9dade 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/ChartConfigure.tsx +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/ChartConfigure.tsx @@ -205,8 +205,10 @@ export const ChartConfigure: React.FC<{ }); }} width={'95%'} - bodyStyle={{ - background: 'rgba(128, 128, 128, 0.08)', + styles={{ + body: { + background: 'rgba(128, 128, 128, 0.08)', + }, }} > @@ -411,7 +413,8 @@ ChartConfigure.Data = function Data() { const error = service?.error; return !error ? ( ({ ...item, _key: index }))} + rowKey="_key" scroll={{ x: 'max-content' }} columns={Object.keys(data[0] || {}).map((col) => { const field = getField(fields, col.split('.')); diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/schemas/configure.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/schemas/configure.ts index 544a9b1b0..523a241d2 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/schemas/configure.ts +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/configure/schemas/configure.ts @@ -312,9 +312,9 @@ export const querySchema: ISchema = { overflow: 'auto', }, }, + enum: '{{ filterOptions }}', 'x-component': 'Filter', 'x-component-props': { - options: '{{ filterOptions }}', dynamicComponent: 'FilterDynamicComponent', }, }, diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/filter/FilterItemInitializers.tsx b/packages/plugins/@nocobase/plugin-data-visualization/src/client/filter/FilterItemInitializers.tsx index eacbe22f5..31faa98f4 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/filter/FilterItemInitializers.tsx +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/filter/FilterItemInitializers.tsx @@ -13,7 +13,7 @@ import { useGlobalTheme, useSchemaInitializerItem, } from '@nocobase/client'; -import React, { useCallback, useContext, useMemo } from 'react'; +import React, { memo, useCallback, useContext, useMemo } from 'react'; import { lang, useChartsTranslation } from '../locale'; import { Schema, SchemaOptionsContext, observer, useField, useFieldSchema, useForm } from '@formily/react'; import { useMemoizedFn } from 'ahooks'; @@ -92,7 +92,7 @@ export const ChartFilterFormItem = observer( export const ChartFilterCustomItemInitializer: React.FC<{ insert?: any; -}> = (props) => { +}> = memo((props) => { const { locale } = useContext(ConfigProvider.ConfigContext); const { t: lang } = useChartsTranslation(); const t = useMemoizedFn(lang); @@ -214,7 +214,7 @@ export const ChartFilterCustomItemInitializer: React.FC<{ // eslint-disable-next-line react-hooks/exhaustive-deps }, [theme]); return ; -}; +}); export const chartFilterItemInitializers = new SchemaInitializer({ name: 'ChartFilterItemInitializers', @@ -232,16 +232,19 @@ export const chartFilterItemInitializers = new SchemaInitializer({ const { getChartCollections } = useChartData(); const { getChartFilterFields } = useChartFilter(); const collections = getChartCollections(); - return collections.map((name: any) => { - const collection = getCollection(name); - const fields = getChartFilterFields(collection); - return { - name: collection.key, - type: 'subMenu', - title: collection.title, - children: fields, - }; - }); + + return useMemo(() => { + return collections.map((name: any) => { + const collection = getCollection(name); + const fields = getChartFilterFields(collection); + return { + name: collection.key, + type: 'subMenu', + title: collection.title, + children: fields, + }; + }); + }, [collections]); }, }, { diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/filter.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/filter.ts index e75c5199d..71e2ee1b4 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/filter.ts +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/filter.ts @@ -412,26 +412,28 @@ export const useChartFilterSourceFields = () => { }; const collections = getChartCollections(); - const options = []; - collections.forEach((name) => { - const collection = getCollection(name); - const children = []; - const fields = getCollectionFields(collection); - fields.forEach((field) => { - const option = field2option(field, 1); - if (option) { - children.push(option); + return useMemo(() => { + const options = []; + collections.forEach((name) => { + const collection = getCollection(name); + const children = []; + const fields = getCollectionFields(collection); + fields.forEach((field) => { + const option = field2option(field, 1); + if (option) { + children.push(option); + } + }); + if (children.length) { + options.push({ + value: name, + label: t(collection.title), + children, + }); } }); - if (children.length) { - options.push({ - value: name, - label: t(collection.title), - children, - }); - } - }); - return options; + return options; + }, [collections]); }; export const useFieldComponents = () => { diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts index 0a1c3f54d..fca5e53d6 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/client/hooks/query.ts @@ -1,7 +1,7 @@ import { ArrayField } from '@formily/core'; import { ISchema, Schema, useForm } from '@formily/react'; import { CollectionFieldOptions, useACLRoleContext, useCollectionManager } from '@nocobase/client'; -import { useContext } from 'react'; +import { useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { ChartConfigContext } from '../configure'; import formatters from '../block/formatters'; @@ -51,43 +51,47 @@ export const useFieldsWithAssociation = (collection?: string) => { const { getCollectionFields, getInterface } = useCollectionManager(); const { t } = useTranslation(); const fields = useFields(collection); - return fields.map((field) => { - const filterable = getInterface(field.interface)?.filterable; - const label = Schema.compile(field.uiSchema?.title || field.name, { t }); - if (!(filterable && (filterable?.nested || filterable?.children?.length))) { - return { ...field, label }; - } - let targetFields = []; - if (filterable?.nested) { - const nestedFields = (getCollectionFields(field.target) || []) - .filter((targetField) => { - return targetField.interface; - }) - .map((targetField) => ({ - ...targetField, - key: `${field.name}.${targetField.name}`, - label: `${label} / ${Schema.compile(targetField.uiSchema?.title || targetField.name, { t })}`, - value: `${field.name}.${targetField.name}`, - })); - targetFields = [...targetFields, ...nestedFields]; - } + return useMemo( + () => + fields.map((field) => { + const filterable = getInterface(field.interface)?.filterable; + const label = Schema.compile(field.uiSchema?.title || field.name, { t }); + if (!(filterable && (filterable?.nested || filterable?.children?.length))) { + return { ...field, label }; + } + let targetFields = []; + if (filterable?.nested) { + const nestedFields = (getCollectionFields(field.target) || []) + .filter((targetField) => { + return targetField.interface; + }) + .map((targetField) => ({ + ...targetField, + key: `${field.name}.${targetField.name}`, + label: `${label} / ${Schema.compile(targetField.uiSchema?.title || targetField.name, { t })}`, + value: `${field.name}.${targetField.name}`, + })); + targetFields = [...targetFields, ...nestedFields]; + } - if (filterable?.children?.length) { - const children = filterable.children.map((child: any) => ({ - ...child, - key: `${field.name}.${child.name}`, - label: `${label} / ${Schema.compile(child.schema?.title || child.title || child.name, { t })}`, - value: `${field.name}.${child.name}`, - })); - targetFields = [...targetFields, ...children]; - } + if (filterable?.children?.length) { + const children = filterable.children.map((child: any) => ({ + ...child, + key: `${field.name}.${child.name}`, + label: `${label} / ${Schema.compile(child.schema?.title || child.title || child.name, { t })}`, + value: `${field.name}.${child.name}`, + })); + targetFields = [...targetFields, ...children]; + } - return { - ...field, - label, - targetFields, - }; - }); + return { + ...field, + label, + targetFields, + }; + }), + [fields], + ); }; export const useChartFields = (fields: FieldOption[]) => (field: any) => { @@ -139,7 +143,7 @@ export const useCollectionOptions = () => { value: collection.name, key: collection.name, })); - return Schema.compile(options, { t }); + return useMemo(() => Schema.compile(options, { t }), [options]); }; export const useOrderFieldsOptions = (defaultOptions: any[], fields: FieldOption[]) => (field: any) => {