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