@hsu-react/ui 2.5.6 → 2.5.7

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.
Files changed (39) hide show
  1. package/es/components/Checkbox/CheckboxGroup/index.js +4 -1
  2. package/es/components/FormItem/FormCodeMirror/index.js +3 -1
  3. package/es/components/FormItem/ItemContainer/index.js +15 -5
  4. package/es/components/Operate/index.js +4 -1
  5. package/es/components/Pagination/index.js +3 -1
  6. package/es/components/Search/_hooks/useSearchCommon.js +4 -1
  7. package/es/components/Select/AutoCompleteSelect/index.js +3 -1
  8. package/es/components/Spreadsheet/index.js +8 -2
  9. package/es/components/Table/_hooks/useTableColumns.js +4 -1
  10. package/es/components/Tree/_hooks/useTreeState.js +7 -4
  11. package/es/components/Tree/index.js +8 -2
  12. package/lib/components/Checkbox/CheckboxGroup/index.js +4 -1
  13. package/lib/components/FormItem/FormCodeMirror/index.js +3 -1
  14. package/lib/components/FormItem/ItemContainer/index.js +16 -6
  15. package/lib/components/Operate/index.js +4 -1
  16. package/lib/components/Pagination/index.js +3 -1
  17. package/lib/components/Search/_hooks/useSearchCommon.js +4 -1
  18. package/lib/components/Select/AutoCompleteSelect/index.js +3 -1
  19. package/lib/components/Spreadsheet/index.js +8 -2
  20. package/lib/components/Table/_hooks/useTableColumns.js +4 -1
  21. package/lib/components/Tree/_hooks/useTreeState.js +6 -3
  22. package/lib/components/Tree/index.js +8 -2
  23. package/package.json +1 -1
  24. package/src/__tests__/depsScan.ts +62 -15
  25. package/src/__tests__/freshObjectDeps.test.tsx +380 -0
  26. package/src/__tests__/freshObjectDepsGuard.test.ts +54 -23
  27. package/src/components/Checkbox/CheckboxGroup/index.tsx +4 -1
  28. package/src/components/FormItem/FormCodeMirror/index.tsx +4 -1
  29. package/src/components/FormItem/ItemContainer/index.tsx +21 -6
  30. package/src/components/Operate/index.tsx +4 -1
  31. package/src/components/Pagination/index.tsx +4 -1
  32. package/src/components/Search/_hooks/useSearchCommon.ts +4 -1
  33. package/src/components/Select/AutoCompleteSelect/index.tsx +4 -1
  34. package/src/components/Spreadsheet/depsMemo.test.tsx +95 -0
  35. package/src/components/Spreadsheet/index.tsx +9 -2
  36. package/src/components/Table/_hooks/useTableColumns.tsx +4 -1
  37. package/src/components/Tree/_hooks/useTreeState.ts +14 -8
  38. package/src/components/Tree/depsMemo.test.tsx +64 -0
  39. package/src/components/Tree/index.tsx +9 -2
@@ -14,12 +14,15 @@ import styles from "./index.module.scss";
14
14
  import { jsx as _jsx } from "react/jsx-runtime";
15
15
  import { jsxs as _jsxs } from "react/jsx-runtime";
16
16
  var Group = Checkbox.Group;
17
+
18
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
19
+ var EMPTY_OPTIONS = [];
17
20
  var CheckboxGroup = function CheckboxGroup(props) {
18
21
  var className = props.className,
19
22
  outline = props.outline,
20
23
  hasAll = props.hasAll,
21
24
  _props$options = props.options,
22
- options = _props$options === void 0 ? [] : _props$options,
25
+ options = _props$options === void 0 ? EMPTY_OPTIONS : _props$options,
23
26
  value = props.value,
24
27
  onChange = props.onChange,
25
28
  _props$layout = props.layout,
@@ -22,6 +22,8 @@ import { Form } from "antd";
22
22
  import CodeMirror from "../../CodeMirror";
23
23
  import ItemContainer from "../ItemContainer";
24
24
  import { jsx as _jsx } from "react/jsx-runtime";
25
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
26
+ var EMPTY_RULES = [];
25
27
  var FormCodeMirror = function FormCodeMirror(props) {
26
28
  var _componentProps$readO;
27
29
  var _props$componentProps = props.componentProps,
@@ -29,7 +31,7 @@ var FormCodeMirror = function FormCodeMirror(props) {
29
31
  itemClassName = props.className,
30
32
  disabled = props.disabled,
31
33
  _props$rules = props.rules,
32
- rules = _props$rules === void 0 ? [] : _props$rules,
34
+ rules = _props$rules === void 0 ? EMPTY_RULES : _props$rules,
33
35
  name = props.name,
34
36
  formItemProps = _objectWithoutProperties(props, _excluded);
35
37
  var enableLint = componentProps.enableLint !== false;
@@ -24,10 +24,13 @@ import usePermissions from "../../../hooks/usePermissions";
24
24
  import { isLegacyHasSelectorBrowser } from "../../../utils/cssSupports";
25
25
  import { generateRandomStr } from "hsu-utils";
26
26
  import useLabelSize from "./_hooks/useLabelSize";
27
+ import useShallowStable from "../../../hooks/useShallowStable";
27
28
  import useInputSize from "./_hooks/useInputSize";
28
29
  import { jsx as _jsx } from "react/jsx-runtime";
29
30
  import { Fragment as _Fragment } from "react/jsx-runtime";
30
31
  import { jsxs as _jsxs } from "react/jsx-runtime";
32
+ // 解构默认值写成字面量会每次渲染新建一个对象,进依赖数组就让 memo 恒不命中;提到模块级常量
33
+ var EMPTY_TIPS = {};
31
34
  var ItemContainer = function ItemContainer(props) {
32
35
  var children = props.children,
33
36
  className = props.className,
@@ -55,7 +58,7 @@ var ItemContainer = function ItemContainer(props) {
55
58
  label = props.label,
56
59
  labelRender = props.labelRender,
57
60
  _props$tips = props.tips,
58
- tips = _props$tips === void 0 ? {} : _props$tips,
61
+ tips = _props$tips === void 0 ? EMPTY_TIPS : _props$tips,
59
62
  _props$horizontalAlig = props.horizontalAlignment,
60
63
  horizontalAlignment = _props$horizontalAlig === void 0 ? "start" : _props$horizontalAlig,
61
64
  _props$hideRequired = props.hideRequired,
@@ -80,7 +83,14 @@ var ItemContainer = function ItemContainer(props) {
80
83
  var _tips$icon = tips.icon,
81
84
  icon = _tips$icon === void 0 ? "material-symbols:help" : _tips$icon,
82
85
  iconClassName = tips.iconClassName,
83
- tipsConfig = _objectWithoutProperties(tips, _excluded2);
86
+ restTipsConfig = _objectWithoutProperties(tips, _excluded2);
87
+ // rest 解构出来的对象每次渲染都是新引用,直接进依赖数组会让 memo 恒不命中。
88
+ // useShallowStable 让它回到值语义:内容浅相等就复用同一引用,真变了立刻透出新引用。
89
+ var tipsConfig = useShallowStable(restTipsConfig);
90
+ // memo 只需要知道「有没有配 tips」,把对象本身放进依赖数组等于按引用比:
91
+ // 消费方写内联 `tips={{ ... }}`(最常见的写法)就让 label 的 memo 恒不命中。
92
+ // 拆成布尔量后依赖回到值语义。
93
+ var hasTips = Object.keys(tips).length > 0;
84
94
  var _usePermissions = usePermissions(hasPermi),
85
95
  permitted = _usePermissions.permitted;
86
96
  var cls = useMemo(function () {
@@ -91,7 +101,7 @@ var ItemContainer = function ItemContainer(props) {
91
101
  if (hideLabel) {
92
102
  return undefined;
93
103
  }
94
- return Object.keys(tips).length ? /*#__PURE__*/_jsxs(_Fragment, {
104
+ return hasTips ? /*#__PURE__*/_jsxs(_Fragment, {
95
105
  children: [/*#__PURE__*/_jsx("span", {
96
106
  className: classNames(styles.labelContent, labelClassName),
97
107
  children: /*#__PURE__*/_jsx("span", {
@@ -137,7 +147,7 @@ var ItemContainer = function ItemContainer(props) {
137
147
  children: labelExtra
138
148
  })]
139
149
  }) : undefined;
140
- }, [tips, labelClassName, label, tipsConfig, icon, iconClassName, labelExtra, labelExtraClassName, labelEllipsis, labelBreak, hideLabel]);
150
+ }, [hasTips, labelClassName, label, tipsConfig, icon, iconClassName, labelExtra, labelExtraClassName, labelEllipsis, labelBreak, hideLabel]);
141
151
  var _useLabelSize = useLabelSize({
142
152
  labelWidth: labelWidth,
143
153
  labelHeight: labelHeight,
@@ -167,7 +177,7 @@ var ItemContainer = function ItemContainer(props) {
167
177
  required: required,
168
178
  message: requiredMsg !== null && requiredMsg !== void 0 ? requiredMsg : en ? "".concat(PlaceholderDictEn[type || "INPUT"]) : "".concat(PlaceholderDict[type || "INPUT"])
169
179
  }].concat(_toConsumableArray(rules)),
170
- className: classNames(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, cls, true), styles.formItem, true), className !== null && className !== void 0 ? className : "", true), styles[layout], layout), styles[requiredPosition], requiredPosition), styles.hideRequired, hideRequired), styles[horizontalAlignment], layout === "horizontal" && horizontalAlignment), styles.hasLabelContent, !!Object.keys(tips).length), styles.hideAdditiona, hideAdditiona), styles.legacyHasLabelExtra, legacyHasSelector && layout === "vertical" && !hideLabel && !!label && !!labelExtra && !Object.keys(tips).length)),
180
+ className: classNames(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, cls, true), styles.formItem, true), className !== null && className !== void 0 ? className : "", true), styles[layout], layout), styles[requiredPosition], requiredPosition), styles.hideRequired, hideRequired), styles[horizontalAlignment], layout === "horizontal" && horizontalAlignment), styles.hasLabelContent, hasTips), styles.hideAdditiona, hideAdditiona), styles.legacyHasLabelExtra, legacyHasSelector && layout === "vertical" && !hideLabel && !!label && !!labelExtra && !hasTips)),
171
181
  colon: typeof colon === "boolean" ? colon : layout === "horizontal" && requiredPosition === "before",
172
182
  label: labelRender ? labelRender(_label) : _label,
173
183
  style: _objectSpread({
@@ -25,6 +25,9 @@ import classNames from "classnames";
25
25
  import moreImg from "./more.png";
26
26
  import { jsx as _jsx } from "react/jsx-runtime";
27
27
  import { jsxs as _jsxs } from "react/jsx-runtime";
28
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
29
+ var EMPTY_MENU = [];
30
+
28
31
  /**
29
32
  * Render an icon
30
33
  * @param icon - Icon configuration
@@ -51,7 +54,7 @@ var Operate = function Operate(props) {
51
54
  var title = props.title,
52
55
  className = props.className,
53
56
  _props$menu = props.menu,
54
- menu = _props$menu === void 0 ? [] : _props$menu,
57
+ menu = _props$menu === void 0 ? EMPTY_MENU : _props$menu,
55
58
  icon = props.icon,
56
59
  isDelete = props.delete,
57
60
  popconfirm = props.popconfirm,
@@ -18,11 +18,13 @@ import styles from "./index.module.scss";
18
18
  import usePaginationSync from "./_hooks/usePaginationSync";
19
19
  import { jsx as _jsx } from "react/jsx-runtime";
20
20
  import { jsxs as _jsxs } from "react/jsx-runtime";
21
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
22
+ var EMPTY_PAGE_SIZE_OPTIONS = [];
21
23
  var Pagination = function Pagination(props) {
22
24
  var _props$current, _props$pageSize2;
23
25
  var simple = props.simple,
24
26
  _props$pageSizeOption = props.pageSizeOptions,
25
- pageSizeOptions = _props$pageSizeOption === void 0 ? [] : _props$pageSizeOption,
27
+ pageSizeOptions = _props$pageSizeOption === void 0 ? EMPTY_PAGE_SIZE_OPTIONS : _props$pageSizeOption,
26
28
  showSizeChanger = props.showSizeChanger,
27
29
  _onShowSizeChange = props.onShowSizeChange,
28
30
  _onChange = props.onChange,
@@ -18,6 +18,9 @@ import { useAdaptiveColumnNum } from "./useAdaptiveColumnNum";
18
18
  import { useSearchExpand } from "./useSearchExpand";
19
19
  import { cleanSearchData, calculateVisibleItems, calculateButtonGroupAndColumnWidth, shouldDisplayExtraItem } from "../_utils";
20
20
  import styles from "../index.module.scss";
21
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
22
+ var EMPTY_SEARCH_ITEMS = [];
23
+
21
24
  /**
22
25
  * Extracts the common logic shared by all Search components
23
26
  */
@@ -25,7 +28,7 @@ export var useSearchCommon = function useSearchCommon(params) {
25
28
  var form = params.form,
26
29
  searchItems = params.searchItems,
27
30
  _params$moreSearchIte = params.moreSearchItems,
28
- moreSearchItems = _params$moreSearchIte === void 0 ? [] : _params$moreSearchIte,
31
+ moreSearchItems = _params$moreSearchIte === void 0 ? EMPTY_SEARCH_ITEMS : _params$moreSearchIte,
29
32
  searchData = params.searchData,
30
33
  hasPermi = params.hasPermi,
31
34
  beforeButtonGroup = params.beforeButtonGroup,
@@ -27,6 +27,8 @@ import Icon from "../../Icon";
27
27
  import { isLegacyHasSelectorBrowser } from "../../../utils/cssSupports";
28
28
  import { jsx as _jsx } from "react/jsx-runtime";
29
29
  import { jsxs as _jsxs } from "react/jsx-runtime";
30
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
31
+ var EMPTY_OPTIONS = [];
30
32
  var AutoCompleteSelect = function AutoCompleteSelect(props) {
31
33
  var prefix = props.prefix,
32
34
  suffix = props.suffix,
@@ -40,7 +42,7 @@ var AutoCompleteSelect = function AutoCompleteSelect(props) {
40
42
  customFilterOption = props.filterOption,
41
43
  onChange = props.onChange,
42
44
  _props$options = props.options,
43
- options = _props$options === void 0 ? [] : _props$options,
45
+ options = _props$options === void 0 ? EMPTY_OPTIONS : _props$options,
44
46
  _props$placement = props.placement,
45
47
  placement = _props$placement === void 0 ? "bottomLeft" : _props$placement,
46
48
  onDropdownVisibleChange = props.onDropdownVisibleChange,
@@ -20,15 +20,21 @@ import { Equal, generateRandomStr } from "hsu-utils";
20
20
  import "x-data-spreadsheet/dist/xspreadsheet.css";
21
21
  import classNames from "classnames";
22
22
  import { stox } from "./xlsxspread";
23
+ import useShallowStable from "../../hooks/useShallowStable";
23
24
  import { jsx as _jsx } from "react/jsx-runtime";
25
+ // 解构默认值写成字面量会每次渲染新建一个对象,进依赖数组就让 effect 每渲染必重跑;提到模块级常量
26
+ var EMPTY_X_OPTIONS = {};
24
27
  var Spreadsheet = function Spreadsheet(props) {
25
28
  var data = props.data,
26
29
  _props$xOptions = props.xOptions,
27
- xOptions = _props$xOptions === void 0 ? {} : _props$xOptions,
30
+ xOptions = _props$xOptions === void 0 ? EMPTY_X_OPTIONS : _props$xOptions,
28
31
  className = props.className;
29
32
  var _xOptions$showBottomT = xOptions.showBottomTool,
30
33
  showBottomTool = _xOptions$showBottomT === void 0 ? true : _xOptions$showBottomT,
31
- xOptionsRest = _objectWithoutProperties(xOptions, _excluded);
34
+ restXOptions = _objectWithoutProperties(xOptions, _excluded);
35
+ // rest 解构出来的对象每次渲染都是新引用,直接进依赖数组会让 memo 恒不命中。
36
+ // useShallowStable 让它回到值语义:内容浅相等就复用同一引用,真变了立刻透出新引用。
37
+ var xOptionsRest = useShallowStable(restXOptions);
32
38
  var ref = useRef(null);
33
39
  var containerRef = useRef(null);
34
40
  var id = useMemo(function () {
@@ -22,6 +22,9 @@ import usePermissions from "../../../hooks/usePermissions";
22
22
  import { jsx as _jsx } from "react/jsx-runtime";
23
23
  import { Fragment as _Fragment } from "react/jsx-runtime";
24
24
  import { jsxs as _jsxs } from "react/jsx-runtime";
25
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
26
+ var EMPTY_COLUMNS = [];
27
+
25
28
  // measureAlign columns: measured at 14px, consistent with the table body and autoWidth title measurement
26
29
  var MEASURE_ALIGN_FONT_SIZE = 14;
27
30
  // Canvas measureText only returns the advance width, about 1px narrower than the actual DOM rendering; add a 2px buffer to avoid squeezing the widest row
@@ -66,7 +69,7 @@ var getMeasureAlignWidth = function getMeasureAlignWidth(column, dataSource) {
66
69
  };
67
70
  var useTableColumns = function useTableColumns(params) {
68
71
  var _params$columns = params.columns,
69
- columns = _params$columns === void 0 ? [] : _params$columns,
72
+ columns = _params$columns === void 0 ? EMPTY_COLUMNS : _params$columns,
70
73
  scroll = params.scroll,
71
74
  autoWidth = params.autoWidth,
72
75
  dataSource = params.dataSource,
@@ -4,7 +4,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
4
4
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
- import { useEffect, useState, useMemo, useRef } from "react";
7
+ import { useEffect, useState, useMemo, useRef, useCallback } from "react";
8
8
  import { Equal } from "hsu-utils";
9
9
  import { normalizeCheckedKeys } from "../_utils/normalizeCheckedKeys";
10
10
 
@@ -38,11 +38,14 @@ export var useExpandedKeys = function useExpandedKeys(expandedKeysProps, default
38
38
  }
39
39
  }, [defaultExpandedKeys, expandedKeysProps]);
40
40
 
41
- // Wrap setExpandedKeys to mark that the user has manually interacted
42
- var handleSetExpandedKeys = function handleSetExpandedKeys(keys) {
41
+ // Wrap setExpandedKeys to mark that the user has manually interacted.
42
+ // 必须 useCallback:这个函数被 Tree handleExpand 当依赖用,
43
+ // 每渲染新建一个就让那个 useCallback 恒不命中——展开回调每渲染换引用透给 antd Tree。
44
+ // 函数体只碰 ref 和状态 setter,两者都恒定,所以依赖数组是空的。
45
+ var handleSetExpandedKeys = useCallback(function (keys) {
43
46
  hasUserInteractedRef.current = true;
44
47
  setExpandedKeys(keys);
45
- };
48
+ }, []);
46
49
  return [expandedKeys, handleSetExpandedKeys];
47
50
  };
48
51
 
@@ -26,9 +26,12 @@ import { useClearNodeTitle } from "./_hooks/useClearNodeTitle";
26
26
  import { getExpandedKeysByLevel, getNodePath } from "./_utils";
27
27
  import TextEllipsis from "../TextEllipsis";
28
28
  import usePermissions from "../../hooks/usePermissions";
29
+ import useShallowStable from "../../hooks/useShallowStable";
29
30
  import { isLegacyHasSelectorBrowser } from "../../utils/cssSupports";
30
31
  import { jsx as _jsx } from "react/jsx-runtime";
31
32
  import { jsxs as _jsxs } from "react/jsx-runtime";
33
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
34
+ var EMPTY_TREE_DATA = [];
32
35
  var Tree = function Tree(props) {
33
36
  var title = props.title,
34
37
  _titleRender = props.titleRender,
@@ -38,7 +41,7 @@ var Tree = function Tree(props) {
38
41
  className = props.className,
39
42
  search = props.search,
40
43
  _props$treeData = props.treeData,
41
- treeData = _props$treeData === void 0 ? [] : _props$treeData,
44
+ treeData = _props$treeData === void 0 ? EMPTY_TREE_DATA : _props$treeData,
42
45
  onChange = props.onChange,
43
46
  onCheck = props.onCheck,
44
47
  onSelect = props.onSelect,
@@ -61,7 +64,10 @@ var Tree = function Tree(props) {
61
64
  _props$allowDeselect = props.allowDeselect,
62
65
  allowDeselect = _props$allowDeselect === void 0 ? true : _props$allowDeselect,
63
66
  titleSearchBarClassName = props.titleSearchBarClassName,
64
- treeConfig = _objectWithoutProperties(props, _excluded);
67
+ restTreeConfig = _objectWithoutProperties(props, _excluded);
68
+ // rest 解构出来的对象每次渲染都是新引用,直接进依赖数组会让 memo 恒不命中。
69
+ // useShallowStable 让它回到值语义:内容浅相等就复用同一引用,真变了立刻透出新引用。
70
+ var treeConfig = useShallowStable(restTreeConfig);
65
71
  var _usePermissions = usePermissions(hasPermi),
66
72
  permitted = _usePermissions.permitted;
67
73
  var legacyHasSelector = isLegacyHasSelectorBrowser();
@@ -13,12 +13,15 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
13
13
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
14
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
15
  const Group = _antd.Checkbox.Group;
16
+
17
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
18
+ const EMPTY_OPTIONS = [];
16
19
  const CheckboxGroup = props => {
17
20
  const {
18
21
  className,
19
22
  outline,
20
23
  hasAll,
21
- options = [],
24
+ options = EMPTY_OPTIONS,
22
25
  value,
23
26
  onChange,
24
27
  layout = "horizontal",
@@ -10,12 +10,14 @@ var _CodeMirror = _interopRequireDefault(require("../../CodeMirror"));
10
10
  var _ItemContainer = _interopRequireDefault(require("../ItemContainer"));
11
11
  var _jsxRuntime = require("react/jsx-runtime");
12
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
14
+ const EMPTY_RULES = [];
13
15
  const FormCodeMirror = props => {
14
16
  const {
15
17
  componentProps = {},
16
18
  className: itemClassName,
17
19
  disabled,
18
- rules = [],
20
+ rules = EMPTY_RULES,
19
21
  name,
20
22
  ...formItemProps
21
23
  } = props;
@@ -14,11 +14,14 @@ var _usePermissions = _interopRequireDefault(require("../../../hooks/usePermissi
14
14
  var _cssSupports = require("../../../utils/cssSupports");
15
15
  var _hsuUtils = require("hsu-utils");
16
16
  var _useLabelSize = _interopRequireDefault(require("./_hooks/useLabelSize"));
17
+ var _useShallowStable = _interopRequireDefault(require("../../../hooks/useShallowStable"));
17
18
  var _useInputSize = _interopRequireDefault(require("./_hooks/useInputSize"));
18
19
  var _jsxRuntime = require("react/jsx-runtime");
19
20
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
21
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
21
22
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
23
+ // 解构默认值写成字面量会每次渲染新建一个对象,进依赖数组就让 memo 恒不命中;提到模块级常量
24
+ const EMPTY_TIPS = {};
22
25
  const ItemContainer = props => {
23
26
  const {
24
27
  children,
@@ -42,7 +45,7 @@ const ItemContainer = props => {
42
45
  colon,
43
46
  label,
44
47
  labelRender,
45
- tips = {},
48
+ tips = EMPTY_TIPS,
46
49
  horizontalAlignment = "start",
47
50
  hideRequired = false,
48
51
  labelClassName,
@@ -61,8 +64,15 @@ const ItemContainer = props => {
61
64
  const {
62
65
  icon = "material-symbols:help",
63
66
  iconClassName,
64
- ...tipsConfig
67
+ ...restTipsConfig
65
68
  } = tips;
69
+ // rest 解构出来的对象每次渲染都是新引用,直接进依赖数组会让 memo 恒不命中。
70
+ // useShallowStable 让它回到值语义:内容浅相等就复用同一引用,真变了立刻透出新引用。
71
+ const tipsConfig = (0, _useShallowStable.default)(restTipsConfig);
72
+ // memo 只需要知道「有没有配 tips」,把对象本身放进依赖数组等于按引用比:
73
+ // 消费方写内联 `tips={{ ... }}`(最常见的写法)就让 label 的 memo 恒不命中。
74
+ // 拆成布尔量后依赖回到值语义。
75
+ const hasTips = Object.keys(tips).length > 0;
66
76
  const {
67
77
  permitted
68
78
  } = (0, _usePermissions.default)(hasPermi);
@@ -72,7 +82,7 @@ const ItemContainer = props => {
72
82
  if (hideLabel) {
73
83
  return undefined;
74
84
  }
75
- return Object.keys(tips).length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
85
+ return hasTips ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
76
86
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
77
87
  className: (0, _classnames.default)(_indexModule.default.labelContent, labelClassName),
78
88
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
@@ -124,7 +134,7 @@ const ItemContainer = props => {
124
134
  children: labelExtra
125
135
  })]
126
136
  }) : undefined;
127
- }, [tips, labelClassName, label, tipsConfig, icon, iconClassName, labelExtra, labelExtraClassName, labelEllipsis, labelBreak, hideLabel]);
137
+ }, [hasTips, labelClassName, label, tipsConfig, icon, iconClassName, labelExtra, labelExtraClassName, labelEllipsis, labelBreak, hideLabel]);
128
138
  const {
129
139
  width: _labelWidth,
130
140
  height: _labelHeight,
@@ -165,9 +175,9 @@ const ItemContainer = props => {
165
175
  [_indexModule.default[requiredPosition]]: requiredPosition,
166
176
  [_indexModule.default.hideRequired]: hideRequired,
167
177
  [_indexModule.default[horizontalAlignment]]: layout === "horizontal" && horizontalAlignment,
168
- [_indexModule.default.hasLabelContent]: !!Object.keys(tips).length,
178
+ [_indexModule.default.hasLabelContent]: hasTips,
169
179
  [_indexModule.default.hideAdditiona]: hideAdditiona,
170
- [_indexModule.default.legacyHasLabelExtra]: legacyHasSelector && layout === "vertical" && !hideLabel && !!label && !!labelExtra && !Object.keys(tips).length
180
+ [_indexModule.default.legacyHasLabelExtra]: legacyHasSelector && layout === "vertical" && !hideLabel && !!label && !!labelExtra && !hasTips
171
181
  }),
172
182
  colon: typeof colon === "boolean" ? colon : layout === "horizontal" && requiredPosition === "before",
173
183
  label: labelRender ? labelRender(_label) : _label,
@@ -18,6 +18,9 @@ var _jsxRuntime = require("react/jsx-runtime");
18
18
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
19
19
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
20
20
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
22
+ const EMPTY_MENU = [];
23
+
21
24
  /**
22
25
  * Render an icon
23
26
  * @param icon - Icon configuration
@@ -44,7 +47,7 @@ const Operate = props => {
44
47
  const {
45
48
  title,
46
49
  className,
47
- menu = [],
50
+ menu = EMPTY_MENU,
48
51
  icon,
49
52
  delete: isDelete,
50
53
  popconfirm,
@@ -14,10 +14,12 @@ var _jsxRuntime = require("react/jsx-runtime");
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
16
16
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
17
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
18
+ const EMPTY_PAGE_SIZE_OPTIONS = [];
17
19
  const Pagination = props => {
18
20
  const {
19
21
  simple,
20
- pageSizeOptions = [],
22
+ pageSizeOptions = EMPTY_PAGE_SIZE_OPTIONS,
21
23
  showSizeChanger,
22
24
  onShowSizeChange,
23
25
  onChange,
@@ -15,6 +15,9 @@ var _useSearchExpand = require("./useSearchExpand");
15
15
  var _utils = require("../_utils");
16
16
  var _indexModule = _interopRequireDefault(require("../index.module.scss"));
17
17
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
19
+ const EMPTY_SEARCH_ITEMS = [];
20
+
18
21
  /**
19
22
  * Extracts the common logic shared by all Search components
20
23
  */
@@ -22,7 +25,7 @@ const useSearchCommon = params => {
22
25
  const {
23
26
  form,
24
27
  searchItems,
25
- moreSearchItems = [],
28
+ moreSearchItems = EMPTY_SEARCH_ITEMS,
26
29
  searchData,
27
30
  hasPermi,
28
31
  beforeButtonGroup,
@@ -19,6 +19,8 @@ var _jsxRuntime = require("react/jsx-runtime");
19
19
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
20
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
21
21
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
22
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
23
+ const EMPTY_OPTIONS = [];
22
24
  const AutoCompleteSelect = props => {
23
25
  const {
24
26
  prefix,
@@ -32,7 +34,7 @@ const AutoCompleteSelect = props => {
32
34
  popupClassName,
33
35
  filterOption: customFilterOption,
34
36
  onChange,
35
- options = [],
37
+ options = EMPTY_OPTIONS,
36
38
  placement = "bottomLeft",
37
39
  onDropdownVisibleChange,
38
40
  popupMatchSelectWidth,
@@ -11,20 +11,26 @@ var _hsuUtils = require("hsu-utils");
11
11
  require("x-data-spreadsheet/dist/xspreadsheet.css");
12
12
  var _classnames = _interopRequireDefault(require("classnames"));
13
13
  var _xlsxspread = require("./xlsxspread");
14
+ var _useShallowStable = _interopRequireDefault(require("../../hooks/useShallowStable"));
14
15
  var _jsxRuntime = require("react/jsx-runtime");
15
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
17
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
17
18
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
19
+ // 解构默认值写成字面量会每次渲染新建一个对象,进依赖数组就让 effect 每渲染必重跑;提到模块级常量
20
+ const EMPTY_X_OPTIONS = {};
18
21
  const Spreadsheet = props => {
19
22
  const {
20
23
  data,
21
- xOptions = {},
24
+ xOptions = EMPTY_X_OPTIONS,
22
25
  className
23
26
  } = props;
24
27
  const {
25
28
  showBottomTool = true,
26
- ...xOptionsRest
29
+ ...restXOptions
27
30
  } = xOptions;
31
+ // rest 解构出来的对象每次渲染都是新引用,直接进依赖数组会让 memo 恒不命中。
32
+ // useShallowStable 让它回到值语义:内容浅相等就复用同一引用,真变了立刻透出新引用。
33
+ const xOptionsRest = (0, _useShallowStable.default)(restXOptions);
28
34
  const ref = (0, _react.useRef)(null);
29
35
  const containerRef = (0, _react.useRef)(null);
30
36
  const id = (0, _react.useMemo)(() => (0, _hsuUtils.generateRandomStr)(10), []);
@@ -15,6 +15,9 @@ var _tableUtils = require("../_utils/tableUtils");
15
15
  var _usePermissions = _interopRequireDefault(require("../../../hooks/usePermissions"));
16
16
  var _jsxRuntime = require("react/jsx-runtime");
17
17
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
19
+ const EMPTY_COLUMNS = [];
20
+
18
21
  // measureAlign columns: measured at 14px, consistent with the table body and autoWidth title measurement
19
22
  const MEASURE_ALIGN_FONT_SIZE = 14;
20
23
  // Canvas measureText only returns the advance width, about 1px narrower than the actual DOM rendering; add a 2px buffer to avoid squeezing the widest row
@@ -56,7 +59,7 @@ const getMeasureAlignWidth = (column, dataSource) => {
56
59
  };
57
60
  const useTableColumns = params => {
58
61
  const {
59
- columns = [],
62
+ columns = EMPTY_COLUMNS,
60
63
  scroll,
61
64
  autoWidth,
62
65
  dataSource,
@@ -34,11 +34,14 @@ const useExpandedKeys = (expandedKeysProps, defaultExpandedKeys) => {
34
34
  }
35
35
  }, [defaultExpandedKeys, expandedKeysProps]);
36
36
 
37
- // Wrap setExpandedKeys to mark that the user has manually interacted
38
- const handleSetExpandedKeys = keys => {
37
+ // Wrap setExpandedKeys to mark that the user has manually interacted.
38
+ // 必须 useCallback:这个函数被 Tree handleExpand 当依赖用,
39
+ // 每渲染新建一个就让那个 useCallback 恒不命中——展开回调每渲染换引用透给 antd Tree。
40
+ // 函数体只碰 ref 和状态 setter,两者都恒定,所以依赖数组是空的。
41
+ const handleSetExpandedKeys = (0, _react.useCallback)(keys => {
39
42
  hasUserInteractedRef.current = true;
40
43
  setExpandedKeys(keys);
41
- };
44
+ }, []);
42
45
  return [expandedKeys, handleSetExpandedKeys];
43
46
  };
44
47
 
@@ -17,11 +17,14 @@ var _useClearNodeTitle = require("./_hooks/useClearNodeTitle");
17
17
  var _utils = require("./_utils");
18
18
  var _TextEllipsis = _interopRequireDefault(require("../TextEllipsis"));
19
19
  var _usePermissions = _interopRequireDefault(require("../../hooks/usePermissions"));
20
+ var _useShallowStable = _interopRequireDefault(require("../../hooks/useShallowStable"));
20
21
  var _cssSupports = require("../../utils/cssSupports");
21
22
  var _jsxRuntime = require("react/jsx-runtime");
22
23
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
24
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
24
25
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
26
+ // 解构默认值写成字面量会每次渲染新建一个数组,进依赖数组就让 memo 恒不命中;提到模块级常量
27
+ const EMPTY_TREE_DATA = [];
25
28
  const Tree = props => {
26
29
  const {
27
30
  title,
@@ -31,7 +34,7 @@ const Tree = props => {
31
34
  titleClassName,
32
35
  className,
33
36
  search,
34
- treeData = [],
37
+ treeData = EMPTY_TREE_DATA,
35
38
  onChange,
36
39
  onCheck,
37
40
  onSelect,
@@ -52,8 +55,11 @@ const Tree = props => {
52
55
  onSelectPath,
53
56
  allowDeselect = true,
54
57
  titleSearchBarClassName,
55
- ...treeConfig
58
+ ...restTreeConfig
56
59
  } = props;
60
+ // rest 解构出来的对象每次渲染都是新引用,直接进依赖数组会让 memo 恒不命中。
61
+ // useShallowStable 让它回到值语义:内容浅相等就复用同一引用,真变了立刻透出新引用。
62
+ const treeConfig = (0, _useShallowStable.default)(restTreeConfig);
57
63
  const {
58
64
  permitted
59
65
  } = (0, _usePermissions.default)(hasPermi);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hsu-react/ui",
3
- "version": "2.5.6",
3
+ "version": "2.5.7",
4
4
  "description": "一套基于 React + Ant Design 的中后台业务组件库",
5
5
  "license": "MIT",
6
6
  "type": "module",