@bit-sun/business-component 4.2.5-per-alpha.1 → 4.2.5-per-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,12 +48,12 @@ class SortableTable extends React.Component {
48
48
  isDefaultValue: false,
49
49
  defaultValue: [],
50
50
  bsTableCode: '', //设置table 列的标识
51
- };
52
-
51
+ }
53
52
  // 实例变量
54
53
  this.isUnmounted = false;
55
54
  this.requestController = null;
56
55
  this.debounceTimer = null;
56
+ this.cleanupTimer = null;
57
57
 
58
58
  // Modal组件ref,用于直接访问DOM进行清理
59
59
  this.modalRef = React.createRef();
@@ -927,37 +927,47 @@ class SortableTable extends React.Component {
927
927
  onSearchSort: '',
928
928
  });
929
929
 
930
+ // 清理之前的定时器
931
+ if (this.cleanupTimer) {
932
+ clearTimeout(this.cleanupTimer);
933
+ this.cleanupTimer = null;
934
+ }
935
+
930
936
  // 强制清理React Fiber节点和DOM引用
931
- setTimeout(() => {
937
+ this.cleanupTimer = setTimeout(() => {
932
938
  try {
933
- // 清理所有带有sort_table类名的DOM节点的React引用
934
- const sortTableElements = document.querySelectorAll('.sort_table, .sort_table_column_wrapper, .sort_table_column, .sort_table_content_wrapper, .sort_table_column_count');
935
- sortTableElements.forEach(element => {
936
- // 清理React内部实例引用
937
- const reactKeys = Object.keys(element).filter(key => key.startsWith('__reactInternalInstance') || key.startsWith('__reactEventHandlers'));
938
- reactKeys.forEach(key => {
939
- try {
940
- delete element[key];
941
- } catch (e) {
942
- // 忽略删除失败
939
+ if (!this.isUnmounted) {
940
+ // 清理所有带有sort_table类名的DOM节点的React引用
941
+ const sortTableElements = document.querySelectorAll('.sort_table, .sort_table_column_wrapper, .sort_table_column, .sort_table_content_wrapper, .sort_table_column_count');
942
+ sortTableElements.forEach(element => {
943
+ // 清理React内部实例引用
944
+ const reactKeys = Object.keys(element).filter(key => key.startsWith('__reactInternalInstance') || key.startsWith('__reactEventHandlers'));
945
+ reactKeys.forEach(key => {
946
+ try {
947
+ delete element[key];
948
+ } catch (e) {
949
+ // 忽略删除失败
950
+ }
951
+ });
952
+
953
+ // 清理React Fiber引用
954
+ if (element._reactInternalFiber) {
955
+ element._reactInternalFiber = null;
956
+ }
957
+ if (element._reactInternalInstance) {
958
+ element._reactInternalInstance = null;
943
959
  }
944
960
  });
945
961
 
946
- // 清理React Fiber引用
947
- if (element._reactInternalFiber) {
948
- element._reactInternalFiber = null;
949
- }
950
- if (element._reactInternalInstance) {
951
- element._reactInternalInstance = null;
962
+ // 强制垃圾回收提示
963
+ if (window.gc) {
964
+ window.gc();
952
965
  }
953
- });
954
-
955
- // 强制垃圾回收提示
956
- if (window.gc) {
957
- window.gc();
958
966
  }
959
967
  } catch (error) {
960
968
  console.warn('[SortableTable] Modal afterClose清理失败:', error);
969
+ } finally {
970
+ this.cleanupTimer = null;
961
971
  }
962
972
  }, 100);
963
973
  }}
@@ -1,5 +1,5 @@
1
1
  // @ts-nocheck
2
- import React, { useEffect, useState, useMemo } from 'react';
2
+ import React, { useEffect, useState, useMemo, useCallback } from 'react';
3
3
  import { memoizeOneFormatter } from '@/utils/utils';
4
4
  import { Breadcrumb, Space } from 'antd';
5
5
  import { useLocation, formatMessage } from 'umi';
@@ -10,11 +10,10 @@ import './index.less';
10
10
 
11
11
  export default (props: any) => {
12
12
  const { pathname } = useLocation();
13
- const [id]: any = useState(pathname + 'id');
14
13
  const { children, ...restProps} = props;
15
14
 
16
15
  return (
17
- <div id={id} className={'home_page_wrapper'}>
16
+ <div id="home-page-wrapper" className={'home_page_wrapper'}>
18
17
  <HeaderWrapper
19
18
  pathname={pathname}
20
19
  {
@@ -37,6 +36,7 @@ const HeaderWrapper = React.memo(
37
36
  pathToRegexp, //改为从项目代码透传该方法
38
37
  }: { pathname: string, routes: any[], itemPath: string, alertProps: any }) => {
39
38
  const [breadcrumbArr, setBreadCrumbArr]: any = useState([]);
39
+ const isMountedRef = React.useRef(true);
40
40
  const basePath = window.top == window ? '' : `/${itemPath}`;
41
41
 
42
42
  const menuRoutes = useMemo(() => {
@@ -49,15 +49,15 @@ const HeaderWrapper = React.memo(
49
49
  return getMainCrumbNameMap(memoizeOneFormatter(menuRoutes, ''));
50
50
  }, [menuRoutes]); // 只有menuRoutes变化时才重新创建
51
51
 
52
- const matchParamsPath = (pathname: string, breadcrumbNameMap: object) => {
52
+ const matchParamsPath = useCallback((pathname: string, breadcrumbNameMap: object) => {
53
53
  const pathKey: any = Object.keys(breadcrumbNameMap).find((key) =>
54
54
  pathToRegexp ? pathToRegexp(key).test(pathname) : false
55
55
  );
56
56
 
57
57
  return pathKey ? breadcrumbNameMap[pathKey] : undefined;
58
- };
58
+ }, [pathToRegexp]);
59
59
 
60
- const getPageTitle = (pathname: string) => {
60
+ const getPageTitle = useCallback((pathname: string) => {
61
61
  const currRouterData = matchParamsPath(
62
62
  `${basePath}${pathname}`,
63
63
  breadcrumbNameMap,
@@ -72,18 +72,39 @@ const HeaderWrapper = React.memo(
72
72
  breadcrumbArrs.pop();
73
73
  breadcrumbArrs.push(title)
74
74
  }
75
- setBreadCrumbArr([...breadcrumbArrs])
75
+ // 只在组件挂载时更新状态,避免内存泄漏
76
+ if (isMountedRef.current) {
77
+ setBreadCrumbArr([...breadcrumbArrs]);
78
+ }
76
79
 
77
80
  const pageName = formatMessage({
78
81
  id: currRouterData.locale || currRouterData.name,
79
82
  defaultMessage: currRouterData.name,
80
83
  });
81
84
  return title ? title : window.top === window ? pageName : `${currRouterData.name}`;
82
- };
85
+ }, [matchParamsPath, breadcrumbNameMap, basePath, title]);
86
+
87
+ const pageTitle = useMemo(() => getPageTitle(pathname), [getPageTitle, pathname]);
83
88
 
84
- const pageTitle = useMemo(() => getPageTitle(pathname), [pathname, title]);
89
+ useEffect(() => {
90
+ // 路径变化时的清理逻辑
91
+ return () => {
92
+ // 只在组件仍挂载时清理状态
93
+ if (isMountedRef.current) {
94
+ setBreadCrumbArr([]);
95
+ }
96
+ };
97
+ }, [pathname]);
85
98
 
86
- useEffect(() => {}, [pathname]);
99
+ // 组件卸载时清理所有引用
100
+ useEffect(() => {
101
+ return () => {
102
+ // 标记组件已卸载
103
+ isMountedRef.current = false;
104
+ // 清理所有状态和引用
105
+ setBreadCrumbArr([]);
106
+ };
107
+ }, []);
87
108
 
88
109
  return (
89
110
  <div className='bs_home_page_head_wrapper'>
@@ -124,9 +145,18 @@ const HeaderWrapper = React.memo(
124
145
  );
125
146
  },
126
147
  (prevProps, nextProps) => {
127
- // if (prevProps.pathname !== nextProps.pathname) {
128
- // return false;
129
- // }
148
+ // 如果关键属性发生变化,则需要重新渲染
149
+ if (
150
+ prevProps.pathname !== nextProps.pathname ||
151
+ prevProps.title !== nextProps.title ||
152
+ prevProps.itemPath !== nextProps.itemPath ||
153
+ prevProps.routes !== nextProps.routes ||
154
+ prevProps.pathToRegexp !== nextProps.pathToRegexp ||
155
+ JSON.stringify(prevProps.alertProps) !== JSON.stringify(nextProps.alertProps) ||
156
+ prevProps.extra !== nextProps.extra
157
+ ) {
158
+ return false;
159
+ }
130
160
  return true;
131
161
  },
132
162
  );
@@ -33,6 +33,8 @@ export default props => {
33
33
  } = props;
34
34
  const [treeData, setTreeData] = useState(parseData(data));
35
35
  const [activeLine, setActiveLine] = useState(-1);
36
+ const highlightTimerRef = React.useRef(null);
37
+ const isUnmountedRef = React.useRef(false);
36
38
 
37
39
  useEffect(() => {
38
40
  setTreeData(parseData(data));
@@ -47,10 +49,35 @@ export default props => {
47
49
  }, [treeData, currentLine]);
48
50
 
49
51
  useEffect(() => {
50
- setTimeout(() => {
51
- setTreeData(createHighLightTreeData(treeData, activeLine));
52
+ // 清理之前的定时器
53
+ if (highlightTimerRef.current) {
54
+ clearTimeout(highlightTimerRef.current);
55
+ highlightTimerRef.current = null;
56
+ }
57
+
58
+ highlightTimerRef.current = setTimeout(() => {
59
+ try {
60
+ if (!isUnmountedRef.current) {
61
+ setTreeData(createHighLightTreeData(treeData, activeLine));
62
+ }
63
+ } catch (error) {
64
+ console.warn('Failed to update tree highlight:', error);
65
+ } finally {
66
+ highlightTimerRef.current = null;
67
+ }
52
68
  });
53
69
  }, [activeLine]);
70
+
71
+ // 组件卸载时清理定时器
72
+ useEffect(() => {
73
+ return () => {
74
+ isUnmountedRef.current = true;
75
+ if (highlightTimerRef.current) {
76
+ clearTimeout(highlightTimerRef.current);
77
+ highlightTimerRef.current = null;
78
+ }
79
+ };
80
+ }, []);
54
81
 
55
82
  const handleSelect = (node, hasChildren) => {
56
83
  const noActiveData = clearActiveNode(treeData);
@@ -1,4 +1,5 @@
1
- import React, { useState, useEffect } from 'react';
1
+ // @ts-nocheck
2
+ import React, { useState, useEffect, useRef } from 'react';
2
3
  import { useDebounceFn } from 'ahooks';
3
4
  import _, { isNil } from "lodash"
4
5
  import { Select, Input, Button, Modal, ConfigProvider, Spin, message } from 'antd';
@@ -45,6 +46,8 @@ const QueryMutipleSearchSelect = ({ onValueChange, requestConfig={}, selectProps
45
46
  const [fetching, setFetching] = useState(false);
46
47
  const [searchValue, setSearchValue] = useState('');
47
48
  const [uniqueValue, setUniqueValue] = useState(resultSourceKey);
49
+ const selectTimerRef = useRef(null);
50
+ const isUnmountedRef = useRef(false);
48
51
 
49
52
  useEffect(() => {
50
53
  setPopValue(value);
@@ -54,6 +57,17 @@ const QueryMutipleSearchSelect = ({ onValueChange, requestConfig={}, selectProps
54
57
  useEffect(() => {
55
58
  setUniqueValue(makeUniqueValue());
56
59
  }, [resultSourceKey])
60
+
61
+ // 组件卸载时清理定时器
62
+ useEffect(() => {
63
+ return () => {
64
+ isUnmountedRef.current = true;
65
+ if (selectTimerRef.current) {
66
+ clearTimeout(selectTimerRef.current);
67
+ selectTimerRef.current = null;
68
+ }
69
+ };
70
+ }, []);
57
71
 
58
72
  const showModal = () => {
59
73
  setIsModalVisible(true);
@@ -155,9 +169,24 @@ const QueryMutipleSearchSelect = ({ onValueChange, requestConfig={}, selectProps
155
169
  onSearch={(v) => onSearchChange(v)}
156
170
  onSelect={() => {
157
171
  if(selectMode) return;
158
- setTimeout(() => {
159
- setOpen(false)
160
- }, 0)
172
+
173
+ // 清理之前的定时器
174
+ if (selectTimerRef.current) {
175
+ clearTimeout(selectTimerRef.current);
176
+ selectTimerRef.current = null;
177
+ }
178
+
179
+ selectTimerRef.current = setTimeout(() => {
180
+ try {
181
+ if (!isUnmountedRef.current) {
182
+ setOpen(false);
183
+ }
184
+ } catch (error) {
185
+ console.warn('Failed to close select:', error);
186
+ } finally {
187
+ selectTimerRef.current = null;
188
+ }
189
+ }, 0);
161
190
  }}
162
191
  style={{ width: 'calc(100%)' }}
163
192
  suffixIcon={<div className={`query_select_expand_button`} onClick={showModal}><DashOutlined /></div>}