@hsu-react/ui 2.5.3 → 2.5.4

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 (57) hide show
  1. package/es/components/Chart/Bubble/index.js +7 -2
  2. package/es/components/Chart/Common/index.js +7 -2
  3. package/es/components/Chart/Heatmap/index.js +7 -2
  4. package/es/components/Chart/Pie/Pie3D/index.js +8 -2
  5. package/es/components/Chart/Pie/index.js +7 -2
  6. package/es/components/Chat/ChatInput/index.js +7 -2
  7. package/es/components/Icon/index.js +8 -2
  8. package/es/components/Input/Number/index.js +28 -21
  9. package/es/components/Input/Password/index.js +44 -36
  10. package/es/components/Input/Search/index.js +44 -36
  11. package/es/components/Input/TextArea/index.js +64 -42
  12. package/es/components/Input/index.js +40 -34
  13. package/es/components/Select/_hooks/useSelectInputOptions.js +8 -2
  14. package/es/components/Slider/index.js +24 -15
  15. package/es/components/Upload/_hooks/useUploadFileList.js +8 -2
  16. package/es/components/Upload/_hooks/useUploadOperations.js +8 -2
  17. package/es/hooks/useLatestRef.d.ts +24 -0
  18. package/es/hooks/useLatestRef.js +29 -0
  19. package/lib/components/Chart/Bubble/index.js +6 -2
  20. package/lib/components/Chart/Common/index.js +6 -2
  21. package/lib/components/Chart/Heatmap/index.js +6 -2
  22. package/lib/components/Chart/Pie/Pie3D/index.js +7 -2
  23. package/lib/components/Chart/Pie/index.js +6 -2
  24. package/lib/components/Chat/ChatInput/index.js +6 -2
  25. package/lib/components/Icon/index.js +7 -2
  26. package/lib/components/Input/Number/index.js +27 -18
  27. package/lib/components/Input/Password/index.js +40 -26
  28. package/lib/components/Input/Search/index.js +42 -26
  29. package/lib/components/Input/TextArea/index.js +54 -26
  30. package/lib/components/Input/index.js +37 -24
  31. package/lib/components/Select/_hooks/useSelectInputOptions.js +7 -2
  32. package/lib/components/Slider/index.js +23 -11
  33. package/lib/components/Upload/_hooks/useUploadFileList.js +7 -2
  34. package/lib/components/Upload/_hooks/useUploadOperations.js +7 -2
  35. package/lib/hooks/useLatestRef.d.ts +24 -0
  36. package/lib/hooks/useLatestRef.js +35 -0
  37. package/package.json +9 -3
  38. package/src/__tests__/setup.ts +26 -0
  39. package/src/components/Chart/Bubble/index.tsx +7 -2
  40. package/src/components/Chart/Common/index.tsx +7 -2
  41. package/src/components/Chart/Heatmap/index.tsx +7 -2
  42. package/src/components/Chart/Pie/Pie3D/index.tsx +7 -2
  43. package/src/components/Chart/Pie/index.tsx +7 -2
  44. package/src/components/Chat/ChatInput/index.tsx +7 -2
  45. package/src/components/Icon/index.tsx +7 -2
  46. package/src/components/Input/Number/index.tsx +25 -23
  47. package/src/components/Input/Password/index.tsx +37 -26
  48. package/src/components/Input/Search/index.tsx +39 -26
  49. package/src/components/Input/TextArea/index.test.tsx +208 -0
  50. package/src/components/Input/TextArea/index.tsx +56 -30
  51. package/src/components/Input/index.test.tsx +189 -0
  52. package/src/components/Input/index.tsx +36 -29
  53. package/src/components/Select/_hooks/useSelectInputOptions.ts +6 -9
  54. package/src/components/Slider/index.tsx +22 -12
  55. package/src/components/Upload/_hooks/useUploadFileList.ts +6 -2
  56. package/src/components/Upload/_hooks/useUploadOperations.ts +7 -2
  57. package/src/hooks/useLatestRef.ts +31 -0
@@ -21,6 +21,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
21
21
  import styles from "../index.module.scss";
22
22
  import * as echarts from "echarts";
23
23
  import { defaultBubbleColorList, drawCircles } from "../_utils/bubble";
24
+ import { useLatestRef } from "../../../hooks/useLatestRef";
24
25
 
25
26
  // Extended data item type containing all properties the chart needs
26
27
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -276,7 +277,10 @@ var Bubble = function Bubble(props) {
276
277
  chartInstanceRef.current.setOption(chartOption, true);
277
278
  }
278
279
  }, [chartOption, data]);
280
+ var onChartRef = useLatestRef(onChart);
279
281
 
282
+ // 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
283
+ // 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
280
284
  // Initialize the chart
281
285
  useEffect(function () {
282
286
  if (!chartRef.current) return;
@@ -286,10 +290,11 @@ var Bubble = function Bubble(props) {
286
290
  // Initialize or reuse the existing instance
287
291
  var chart = chartInstanceRef.current;
288
292
  if (!chart) {
293
+ var _onChartRef$current;
289
294
  chart = echarts.init(chartRef.current);
290
295
  chartInstanceRef.current = chart;
291
296
  // Call the onChart callback on first initialization
292
- onChart === null || onChart === void 0 || onChart(chart);
297
+ (_onChartRef$current = onChartRef.current) === null || _onChartRef$current === void 0 || _onChartRef$current.call(onChartRef, chart);
293
298
  }
294
299
 
295
300
  // Resize first so echarts picks up the correct container size, then apply the option
@@ -313,7 +318,7 @@ var Bubble = function Bubble(props) {
313
318
  (_chartInstanceRef$cur3 = chartInstanceRef.current) === null || _chartInstanceRef$cur3 === void 0 || _chartInstanceRef$cur3.off("click", onClick);
314
319
  }
315
320
  };
316
- }, [chartOption, containerSize.width, containerSize.height, handleResize, onChart, onClick]);
321
+ }, [chartOption, containerSize.width, containerSize.height, handleResize, onChartRef, onClick]);
317
322
 
318
323
  // Dispose the chart instance when the component unmounts
319
324
  useEffect(function () {
@@ -11,6 +11,7 @@ import React, { useCallback, useEffect, useMemo, useRef } from "react";
11
11
  import useContainerReady from "../_hooks/useContainerReady";
12
12
  import styles from "../index.module.scss";
13
13
  import * as echarts from "echarts";
14
+ import { useLatestRef } from "../../../hooks/useLatestRef";
14
15
  import { jsx as _jsx } from "react/jsx-runtime";
15
16
  var Common = function Common(props) {
16
17
  var className = props.className,
@@ -38,9 +39,13 @@ var Common = function Common(props) {
38
39
  if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
39
40
  (_chartInstanceRef$cur = chartInstanceRef.current) === null || _chartInstanceRef$cur === void 0 || _chartInstanceRef$cur.resize();
40
41
  }, []);
42
+ var onChartRef = useLatestRef(onChart);
41
43
 
44
+ // 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
45
+ // 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
42
46
  // Initialize the chart
43
47
  useEffect(function () {
48
+ var _onChartRef$current;
44
49
  if (!chartRef.current || !containerReady) return;
45
50
 
46
51
  // Initialize or reuse an existing instance
@@ -54,7 +59,7 @@ var Common = function Common(props) {
54
59
  chart.setOption(chartOption, true);
55
60
 
56
61
  // Chart-ready callback (can be used for legend auto-scroll, etc.)
57
- onChart === null || onChart === void 0 || onChart(chart);
62
+ (_onChartRef$current = onChartRef.current) === null || _onChartRef$current === void 0 || _onChartRef$current.call(onChartRef, chart);
58
63
 
59
64
  // Add resize listener
60
65
  window.addEventListener("resize", handleResize);
@@ -69,7 +74,7 @@ var Common = function Common(props) {
69
74
  return function () {
70
75
  window.removeEventListener("resize", handleResize);
71
76
  };
72
- }, [chartOption, handleResize, onChart, containerReady]);
77
+ }, [chartOption, handleResize, onChartRef, containerReady]);
73
78
 
74
79
  // Clean up resources on component unmount
75
80
  useEffect(function () {
@@ -20,6 +20,7 @@ import * as echarts from "echarts";
20
20
  import { createDefaultHeatmapXAxis, createDefaultHeatmapYAxis } from "../_utils/heatmap";
21
21
  import { resolveChartChrome } from "../_utils/chartTheme";
22
22
  import useIsDark from "../../../hooks/useIsDark";
23
+ import { useLatestRef } from "../../../hooks/useLatestRef";
23
24
  import { jsx as _jsx } from "react/jsx-runtime";
24
25
  var Heatmap = function Heatmap(props) {
25
26
  var className = props.className,
@@ -192,7 +193,10 @@ var Heatmap = function Heatmap(props) {
192
193
  if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
193
194
  (_chartInstanceRef$cur = chartInstanceRef.current) === null || _chartInstanceRef$cur === void 0 || _chartInstanceRef$cur.resize();
194
195
  }, []);
196
+ var onChartRef = useLatestRef(onChart);
195
197
 
198
+ // 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
199
+ // 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
196
200
  // Initialize the chart
197
201
  useEffect(function () {
198
202
  if (!chartRef.current || !containerReady) return;
@@ -200,10 +204,11 @@ var Heatmap = function Heatmap(props) {
200
204
  // Initialize or reuse the existing instance
201
205
  var chart = chartInstanceRef.current;
202
206
  if (!chart) {
207
+ var _onChartRef$current;
203
208
  chart = echarts.init(chartRef.current);
204
209
  chartInstanceRef.current = chart;
205
210
  // Call the onChart callback on first initialization
206
- onChart === null || onChart === void 0 || onChart(chart);
211
+ (_onChartRef$current = onChartRef.current) === null || _onChartRef$current === void 0 || _onChartRef$current.call(onChartRef, chart);
207
212
  }
208
213
 
209
214
  // Apply the option
@@ -232,7 +237,7 @@ var Heatmap = function Heatmap(props) {
232
237
  (_chartInstanceRef$cur3 = chartInstanceRef.current) === null || _chartInstanceRef$cur3 === void 0 || _chartInstanceRef$cur3.off("click", onClick);
233
238
  }
234
239
  };
235
- }, [chartOption, handleResize, onChart, onClick, containerReady]);
240
+ }, [chartOption, handleResize, onChartRef, onClick, containerReady]);
236
241
 
237
242
  // Clean up resources when the component unmounts
238
243
  useEffect(function () {
@@ -8,6 +8,7 @@ import React, { useCallback, useEffect, useMemo, useRef } from "react";
8
8
  import { useMouseOverHandler, useGlobalOutHandler } from "./_hooks";
9
9
  import { getPie3DOption } from "./_utils/option";
10
10
  import styles from "../../index.module.scss";
11
+ import { useLatestRef } from "../../../../hooks/useLatestRef";
11
12
  import { jsx as _jsx } from "react/jsx-runtime";
12
13
  var ChartPie3D = function ChartPie3D(props) {
13
14
  var className = props.className,
@@ -89,13 +90,18 @@ var ChartPie3D = function ChartPie3D(props) {
89
90
  yOffset: yOffset,
90
91
  autoRotate: autoRotate
91
92
  });
93
+ var onChartRef = useLatestRef(onChart);
94
+
95
+ // 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
96
+ // 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
92
97
  useEffect(function () {
93
98
  if (!chartRef.current || !containerReady) return;
94
99
  var chart = chartInstanceRef.current;
95
100
  if (!chart) {
101
+ var _onChartRef$current;
96
102
  chart = echarts.init(chartRef.current);
97
103
  chartInstanceRef.current = chart;
98
- onChart === null || onChart === void 0 || onChart(chart);
104
+ (_onChartRef$current = onChartRef.current) === null || _onChartRef$current === void 0 || _onChartRef$current.call(onChartRef, chart);
99
105
  }
100
106
  chart.setOption(chartOption, true);
101
107
  optionRef.current = chartOption;
@@ -119,7 +125,7 @@ var ChartPie3D = function ChartPie3D(props) {
119
125
  }
120
126
  }
121
127
  };
122
- }, [chartOption, handleResize, handleMouseOver, handleGlobalOut, onChart, onClick, containerReady]);
128
+ }, [chartOption, handleResize, handleMouseOver, handleGlobalOut, onChartRef, onClick, containerReady]);
123
129
  useEffect(function () {
124
130
  return function () {
125
131
  if (resizeObserverRef.current) {
@@ -19,6 +19,7 @@ import React, { useCallback, useEffect, useMemo, useRef } from "react";
19
19
  import { autoScrollLegend } from "../chartUtils";
20
20
  import styles from "../index.module.scss";
21
21
  import ChartPie3D from "./Pie3D";
22
+ import { useLatestRef } from "../../../hooks/useLatestRef";
22
23
  import { jsx as _jsx } from "react/jsx-runtime";
23
24
  var ChartPie = function ChartPie(props) {
24
25
  var className = props.className,
@@ -207,7 +208,10 @@ var ChartPie = function ChartPie(props) {
207
208
  if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
208
209
  (_chartInstanceRef$cur = chartInstanceRef.current) === null || _chartInstanceRef$cur === void 0 || _chartInstanceRef$cur.resize();
209
210
  }, []);
211
+ var onChartRef = useLatestRef(onChart);
210
212
 
213
+ // 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
214
+ // 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
211
215
  // Initialize the chart
212
216
  useEffect(function () {
213
217
  if (!chartRef.current || !containerReady) return;
@@ -215,10 +219,11 @@ var ChartPie = function ChartPie(props) {
215
219
  // Initialize or reuse the existing instance
216
220
  var chart = chartInstanceRef.current;
217
221
  if (!chart) {
222
+ var _onChartRef$current;
218
223
  chart = echarts.init(chartRef.current);
219
224
  chartInstanceRef.current = chart;
220
225
  // Call the onChart callback on first initialization
221
- onChart === null || onChart === void 0 || onChart(chart);
226
+ (_onChartRef$current = onChartRef.current) === null || _onChartRef$current === void 0 || _onChartRef$current.call(onChartRef, chart);
222
227
  }
223
228
 
224
229
  // Apply the option
@@ -268,7 +273,7 @@ var ChartPie = function ChartPie(props) {
268
273
  legendScrollRef.current = null;
269
274
  }
270
275
  };
271
- }, [chartOption, handleResize, onChart, onClick, enableLegendAutoScroll, seriesData, legendVisibleCount, legendScrollInterval, containerReady]);
276
+ }, [chartOption, handleResize, onChartRef, onClick, enableLegendAutoScroll, seriesData, legendVisibleCount, legendScrollInterval, containerReady]);
272
277
 
273
278
  // Clean up resources when the component unmounts
274
279
  useEffect(function () {
@@ -18,6 +18,7 @@ import { useFileList } from "./_hooks";
18
18
  import { FileList, AgentToggleButton, SendButton, StopButton, UploadButton } from "./_components";
19
19
  import classNames from "classnames";
20
20
  import Button from "../../Button";
21
+ import { useLatestRef } from "../../../hooks/useLatestRef";
21
22
  import { jsx as _jsx } from "react/jsx-runtime";
22
23
  import { jsxs as _jsxs } from "react/jsx-runtime";
23
24
  import { Fragment as _Fragment } from "react/jsx-runtime";
@@ -82,7 +83,10 @@ var ChatInput = function ChatInput(props) {
82
83
  var isControlled = externalAgentsState !== undefined;
83
84
  // Use external state or internal state
84
85
  var agentsState = isControlled ? externalAgentsState : internalAgentsState;
86
+ var onAgentsChangeRef = useLatestRef(onAgentsChange);
85
87
 
88
+ // 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
89
+ // 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
86
90
  // When the model switches, sync the agents state (only in uncontrolled mode)
87
91
  useEffect(function () {
88
92
  if (isControlled) return; // In controlled mode, state is managed externally
@@ -91,10 +95,11 @@ var ChatInput = function ChatInput(props) {
91
95
  return m.value === (modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.modelType);
92
96
  });
93
97
  if (currentModel !== null && currentModel !== void 0 && currentModel.agents && currentModel.agents.length > 0) {
98
+ var _onAgentsChangeRef$cu;
94
99
  setInternalAgentsState(currentModel.agents);
95
- onAgentsChange === null || onAgentsChange === void 0 || onAgentsChange(currentModel.agents);
100
+ (_onAgentsChangeRef$cu = onAgentsChangeRef.current) === null || _onAgentsChangeRef$cu === void 0 || _onAgentsChangeRef$cu.call(onAgentsChangeRef, currentModel.agents);
96
101
  }
97
- }, [modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.modelType, modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.modelList, onAgentsChange, isControlled]);
102
+ }, [modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.modelType, modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.modelList, onAgentsChangeRef, isControlled]);
98
103
 
99
104
  // Based on the currently selected model, filter the feature configs supported by that model
100
105
  var currentModel = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.modelList.find(function (m) {
@@ -12,6 +12,7 @@ import * as AntdIcons from "@ant-design/icons";
12
12
  import React, { useCallback, useEffect, useRef } from "react";
13
13
  import classNames from "classnames";
14
14
  import styles from "./index.module.scss";
15
+ import { useLatestRef } from "../../hooks/useLatestRef";
15
16
  import { jsx as _jsx } from "react/jsx-runtime";
16
17
  /** Check whether it is an AntD icon component name (e.g. "SettingOutlined") and return the corresponding component */
17
18
  var getAntdIcon = function getAntdIcon(icon) {
@@ -46,9 +47,14 @@ var Icon = /*#__PURE__*/React.forwardRef(function (props, forwardedRef) {
46
47
  forwardedRef.current = node;
47
48
  }
48
49
  }, [forwardedRef]);
50
+ var onRefRef = useLatestRef(onRef);
51
+
52
+ // 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
53
+ // 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
49
54
  useEffect(function () {
50
- onRef === null || onRef === void 0 || onRef(ref);
51
- }, [onRef, ref]);
55
+ var _onRefRef$current;
56
+ (_onRefRef$current = onRefRef.current) === null || _onRefRef$current === void 0 || _onRefRef$current.call(onRefRef, ref);
57
+ }, [onRefRef, ref]);
52
58
  var mergedStyle = _objectSpread({
53
59
  color: color,
54
60
  fontSize: typeof fontSize === "number" ? "".concat(fontSize, "px") : fontSize
@@ -18,7 +18,7 @@ import React, { useEffect, useRef, useState } from "react";
18
18
  import { CloseCircleFilled } from "@ant-design/icons";
19
19
  import classNames from "classnames";
20
20
  import styles from "./index.module.scss";
21
- import { useDebounceEffect } from "ahooks";
21
+ import { useLatestRef } from "../../../hooks/useLatestRef";
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";
@@ -40,6 +40,7 @@ var InputNumber = function InputNumber(props) {
40
40
  // `getRef` keeps handing consumers an `HTMLInputElement` exactly as before.
41
41
  var ref = useRef(null);
42
42
  var wrapperRef = useRef(null);
43
+ var getRefRef = useLatestRef(getRef);
43
44
 
44
45
  // On initialization, prefer value, then fall back to defaultValue
45
46
  var initialValue = value !== undefined ? typeof value === "number" ? "".concat(value) : (_value$toString = value === null || value === void 0 ? void 0 : value.toString()) !== null && _value$toString !== void 0 ? _value$toString : "" : defaultValue !== undefined ? typeof defaultValue === "number" ? "".concat(defaultValue) : (_defaultValue$toStrin = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()) !== null && _defaultValue$toStrin !== void 0 ? _defaultValue$toStrin : "" : "";
@@ -47,19 +48,21 @@ var InputNumber = function InputNumber(props) {
47
48
  _useState2 = _slicedToArray(_useState, 2),
48
49
  _value = _useState2[0],
49
50
  setValue = _useState2[1];
50
- var _useState3 = useState(initialValue),
51
- _useState4 = _slicedToArray(_useState3, 2),
52
- lastValue = _useState4[0],
53
- setLastValue = _useState4[1];
54
51
  var prevValueRef = useRef(undefined);
55
- useDebounceEffect(function () {
56
- if (_value !== lastValue) {
57
- setLastValue(_value);
58
- onChange === null || onChange === void 0 || onChange(_value);
59
- }
60
- }, [_value, lastValue, onChange], {
61
- wait: 10
62
- });
52
+ // 最近一次通知出去的文本,代替原来那个用 state 记账的写法
53
+ var notifiedRef = useRef(initialValue);
54
+
55
+ /**
56
+ * `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
57
+ * 所以在事件处理里发,不在 effect 里发。详见 `TextArea/index.tsx` 里那段说明——
58
+ * 旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
59
+ * 会让传内联箭头的消费方死循环。
60
+ */
61
+ var notify = function notify(next) {
62
+ if (next === notifiedRef.current) return;
63
+ notifiedRef.current = next;
64
+ onChange === null || onChange === void 0 || onChange(next);
65
+ };
63
66
  useEffect(function () {
64
67
  // Update internal state only when the external value prop actually changes
65
68
  if (prevValueRef.current !== value) {
@@ -76,30 +79,33 @@ var InputNumber = function InputNumber(props) {
76
79
  // 表示的是同一个数,此时以用户正在敲的那份文本为准。
77
80
  var sameNumber = value !== undefined && value !== null && _value !== "" && Number(value) === Number(_value);
78
81
  if (sameNumber) {
79
- // 只同步「已提交值」,不动正在编辑的文本
80
- setLastValue(_value);
82
+ // 只同步「已通知过的值」,不动正在编辑的文本
83
+ notifiedRef.current = _value;
81
84
  } else if (value !== undefined && value !== null) {
82
85
  var newValue = typeof value === "number" ? "".concat(value) : value === null || value === void 0 ? void 0 : value.toString();
86
+ // 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
87
+ notifiedRef.current = newValue;
83
88
  setValue(newValue);
84
- setLastValue(newValue);
85
89
  } else {
86
90
  // Clear only on initialization or when explicitly set to undefined/null externally
91
+ notifiedRef.current = "";
87
92
  setValue("");
88
- setLastValue("");
89
93
  }
90
94
  }
91
95
  // `_value` 是刻意不进依赖的:这个 effect 只该在**外部** value 变化时跑,
92
96
  // 把它加进来会让每次键入都重跑一遍,等于绕过上面那个 prevValueRef 判断
93
97
  // eslint-disable-next-line react-hooks/exhaustive-deps
94
98
  }, [value]);
99
+
100
+ // 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑
95
101
  useEffect(function () {
96
- getRef === null || getRef === void 0 || getRef(ref.current);
97
- }, [getRef]);
102
+ var _getRefRef$current;
103
+ (_getRefRef$current = getRefRef.current) === null || _getRefRef$current === void 0 || _getRefRef$current.call(getRefRef, ref.current);
104
+ }, [getRefRef]);
98
105
  var handleClear = function handleClear(e) {
99
106
  e.stopPropagation();
100
107
  setValue("");
101
- setLastValue("");
102
- onChange === null || onChange === void 0 || onChange("");
108
+ notify("");
103
109
  };
104
110
  var showClear = allowClear && _value !== "" && _value !== undefined && !disabled;
105
111
  var input = /*#__PURE__*/_jsx(AntdInputNumber, _objectSpread(_objectSpread({
@@ -108,6 +114,7 @@ var InputNumber = function InputNumber(props) {
108
114
  onChange: function onChange(e) {
109
115
  var newValue = e === null || e === undefined ? "" : (typeof e === "number" ? e : e || "").toString();
110
116
  setValue(newValue);
117
+ notify(newValue);
111
118
  },
112
119
  className: classNames(styles.antdInput, className),
113
120
  controls: false,
@@ -17,6 +17,7 @@ import AntdPassword from "antd/es/input/Password";
17
17
  import React, { useEffect, useRef, useState } from "react";
18
18
  import classNames from "classnames";
19
19
  import styles from "./index.module.scss";
20
+ import { useLatestRef } from "../../../hooks/useLatestRef";
20
21
  import { jsx as _jsx } from "react/jsx-runtime";
21
22
  var Password = function Password(props) {
22
23
  var _value$toString, _defaultValue$toStrin;
@@ -26,62 +27,69 @@ var Password = function Password(props) {
26
27
  defaultValue = props.defaultValue,
27
28
  className = props.className,
28
29
  inputConfig = _objectWithoutProperties(props, _excluded);
29
- var _useState = useState(false),
30
- _useState2 = _slicedToArray(_useState, 2),
31
- isComposing = _useState2[0],
32
- setComposing = _useState2[1];
33
30
  var ref = useRef(null);
31
+ var getRefRef = useLatestRef(getRef);
32
+ // 输入法组字期间不往外通知。放 ref 不放 state:它只决定「要不要发通知」,
33
+ // 不参与渲染,而且必须在同一次事件里立刻生效
34
+ var composingRef = useRef(false);
34
35
 
35
36
  // On initialization, prefer value, then fall back to defaultValue
36
37
  var initialValue = value !== undefined ? (_value$toString = value === null || value === void 0 ? void 0 : value.toString()) !== null && _value$toString !== void 0 ? _value$toString : "" : defaultValue !== undefined ? (_defaultValue$toStrin = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()) !== null && _defaultValue$toStrin !== void 0 ? _defaultValue$toStrin : "" : "";
37
- var _useState3 = useState(initialValue),
38
- _useState4 = _slicedToArray(_useState3, 2),
39
- _value = _useState4[0],
40
- setValue = _useState4[1];
41
- var _useState5 = useState(initialValue),
42
- _useState6 = _slicedToArray(_useState5, 2),
43
- lastValue = _useState6[0],
44
- setLastValue = _useState6[1];
38
+ var _useState = useState(initialValue),
39
+ _useState2 = _slicedToArray(_useState, 2),
40
+ _value = _useState2[0],
41
+ setValue = _useState2[1];
45
42
  var prevValueRef = useRef(undefined);
46
- useEffect(function () {
47
- if (!isComposing && _value !== lastValue) {
48
- var trimmedValue = _value.trim();
49
- var finalValue = trimmedValue === "" ? "" : _value;
50
- setLastValue(finalValue);
51
- onChange === null || onChange === void 0 || onChange(finalValue);
52
- }
53
- }, [_value, isComposing, lastValue, onChange]);
43
+ // 最近一次通知出去的原文。只用来去掉 compositionend 与 input 两个事件的重复通知
44
+ var notifiedRef = useRef(initialValue);
45
+
46
+ /**
47
+ * `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
48
+ * 所以在事件处理里发,不在 effect 里发。详见 `TextArea/index.tsx` 里那段说明——
49
+ * 旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
50
+ * 会让传内联箭头的消费方死循环。
51
+ */
52
+ var notify = function notify(next) {
53
+ if (next === notifiedRef.current) return;
54
+ notifiedRef.current = next;
55
+ // 全是空白等同于空
56
+ onChange === null || onChange === void 0 || onChange(next.trim() === "" ? "" : next);
57
+ };
54
58
  useEffect(function () {
55
59
  // Update internal state only when the external value prop actually changes
56
60
  if (prevValueRef.current !== value) {
61
+ var _value$toString2;
57
62
  prevValueRef.current = value;
58
- if (value !== undefined) {
59
- setValue(value === null || value === void 0 ? void 0 : value.toString());
60
- setLastValue(value === null || value === void 0 ? void 0 : value.toString());
61
- } else {
62
- // Clear only on initialization or when explicitly set to undefined externally
63
- setValue("");
64
- setLastValue("");
65
- }
63
+
64
+ // 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
65
+ var next = value !== undefined ? (_value$toString2 = value === null || value === void 0 ? void 0 : value.toString()) !== null && _value$toString2 !== void 0 ? _value$toString2 : "" : "";
66
+ notifiedRef.current = next;
67
+ setValue(next);
66
68
  }
67
69
  }, [value]);
70
+
71
+ // 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑
68
72
  useEffect(function () {
69
- getRef === null || getRef === void 0 || getRef(ref.current);
70
- }, [getRef]);
73
+ var _getRefRef$current;
74
+ (_getRefRef$current = getRefRef.current) === null || _getRefRef$current === void 0 || _getRefRef$current.call(getRefRef, ref.current);
75
+ }, [getRefRef]);
71
76
  return /*#__PURE__*/_jsx(AntdPassword, _objectSpread({
72
77
  className: classNames(styles.antdInput, className),
73
78
  ref: ref,
74
79
  onCompositionStart: function onCompositionStart() {
75
- return setComposing(true);
80
+ composingRef.current = true;
76
81
  },
77
- onCompositionEnd: function onCompositionEnd() {
78
- setTimeout(function () {
79
- setComposing(false);
80
- }, 1);
82
+ onCompositionEnd: function onCompositionEnd(e) {
83
+ composingRef.current = false;
84
+ // 组完字的那一下自己发通知。浏览器之间 compositionend 与 input 的先后
85
+ // 不一致,`notify` 里按原文去重,两种顺序都只会发出一次
86
+ notify(e.currentTarget.value);
81
87
  },
82
88
  value: _value,
83
89
  onChange: function onChange(e) {
84
- setValue(e.target.value);
90
+ var next = e.target.value;
91
+ setValue(next);
92
+ if (!composingRef.current) notify(next);
85
93
  }
86
94
  }, inputConfig));
87
95
  };
@@ -17,6 +17,7 @@ import React, { useEffect, useRef, useState } from "react";
17
17
  import AntdSearch from "antd/es/input/Search";
18
18
  import classNames from "classnames";
19
19
  import styles from "./index.module.scss";
20
+ import { useLatestRef } from "../../../hooks/useLatestRef";
20
21
  import { jsx as _jsx } from "react/jsx-runtime";
21
22
  var Search = function Search(props) {
22
23
  var _value$toString, _defaultValue$toStrin;
@@ -26,62 +27,69 @@ var Search = function Search(props) {
26
27
  defaultValue = props.defaultValue,
27
28
  className = props.className,
28
29
  inputConfig = _objectWithoutProperties(props, _excluded);
29
- var _useState = useState(false),
30
- _useState2 = _slicedToArray(_useState, 2),
31
- isComposing = _useState2[0],
32
- setComposing = _useState2[1];
33
30
  var ref = useRef(null);
31
+ var getRefRef = useLatestRef(getRef);
32
+ // 输入法组字期间不往外通知。放 ref 不放 state:它只决定「要不要发通知」,
33
+ // 不参与渲染,而且必须在同一次事件里立刻生效
34
+ var composingRef = useRef(false);
34
35
 
35
36
  // On initialization, prefer value, then fall back to defaultValue
36
37
  var initialValue = value !== undefined ? (_value$toString = value === null || value === void 0 ? void 0 : value.toString()) !== null && _value$toString !== void 0 ? _value$toString : "" : defaultValue !== undefined ? (_defaultValue$toStrin = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()) !== null && _defaultValue$toStrin !== void 0 ? _defaultValue$toStrin : "" : "";
37
- var _useState3 = useState(initialValue),
38
- _useState4 = _slicedToArray(_useState3, 2),
39
- _value = _useState4[0],
40
- setValue = _useState4[1];
41
- var _useState5 = useState(initialValue),
42
- _useState6 = _slicedToArray(_useState5, 2),
43
- lastValue = _useState6[0],
44
- setLastValue = _useState6[1];
38
+ var _useState = useState(initialValue),
39
+ _useState2 = _slicedToArray(_useState, 2),
40
+ _value = _useState2[0],
41
+ setValue = _useState2[1];
45
42
  var prevValueRef = useRef(undefined);
46
- useEffect(function () {
47
- if (!isComposing && _value !== lastValue) {
48
- var trimmedValue = _value.trim();
49
- var finalValue = trimmedValue === "" ? "" : _value;
50
- setLastValue(finalValue);
51
- onChange === null || onChange === void 0 || onChange(finalValue);
52
- }
53
- }, [_value, isComposing, lastValue, onChange]);
43
+ // 最近一次通知出去的原文。只用来去掉 compositionend 与 input 两个事件的重复通知
44
+ var notifiedRef = useRef(initialValue);
45
+
46
+ /**
47
+ * `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
48
+ * 所以在事件处理里发,不在 effect 里发。详见 `TextArea/index.tsx` 里那段说明——
49
+ * 旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
50
+ * 会让传内联箭头的消费方死循环。
51
+ */
52
+ var notify = function notify(next) {
53
+ if (next === notifiedRef.current) return;
54
+ notifiedRef.current = next;
55
+ // 全是空白等同于空
56
+ onChange === null || onChange === void 0 || onChange(next.trim() === "" ? "" : next);
57
+ };
54
58
  useEffect(function () {
55
59
  // Update internal state only when the external value prop actually changes
56
60
  if (prevValueRef.current !== value) {
61
+ var _value$toString2;
57
62
  prevValueRef.current = value;
58
- if (value !== undefined) {
59
- setValue(value === null || value === void 0 ? void 0 : value.toString());
60
- setLastValue(value === null || value === void 0 ? void 0 : value.toString());
61
- } else {
62
- // Clear only on initialization or when explicitly set to undefined externally
63
- setValue("");
64
- setLastValue("");
65
- }
63
+
64
+ // 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
65
+ var next = value !== undefined ? (_value$toString2 = value === null || value === void 0 ? void 0 : value.toString()) !== null && _value$toString2 !== void 0 ? _value$toString2 : "" : "";
66
+ notifiedRef.current = next;
67
+ setValue(next);
66
68
  }
67
69
  }, [value]);
70
+
71
+ // 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑
68
72
  useEffect(function () {
69
- getRef === null || getRef === void 0 || getRef(ref.current);
70
- }, [getRef]);
73
+ var _getRefRef$current;
74
+ (_getRefRef$current = getRefRef.current) === null || _getRefRef$current === void 0 || _getRefRef$current.call(getRefRef, ref.current);
75
+ }, [getRefRef]);
71
76
  return /*#__PURE__*/_jsx(AntdSearch, _objectSpread({
72
77
  ref: ref,
73
78
  className: classNames(styles.antdInput, className),
74
79
  onCompositionStart: function onCompositionStart() {
75
- return setComposing(true);
80
+ composingRef.current = true;
76
81
  },
77
- onCompositionEnd: function onCompositionEnd() {
78
- setTimeout(function () {
79
- setComposing(false);
80
- }, 1);
82
+ onCompositionEnd: function onCompositionEnd(e) {
83
+ composingRef.current = false;
84
+ // 组完字的那一下自己发通知。浏览器之间 compositionend 与 input 的先后
85
+ // 不一致,`notify` 里按原文去重,两种顺序都只会发出一次
86
+ notify(e.currentTarget.value);
81
87
  },
82
88
  value: _value,
83
89
  onChange: function onChange(e) {
84
- return setValue(e.target.value);
90
+ var next = e.target.value;
91
+ setValue(next);
92
+ if (!composingRef.current) notify(next);
85
93
  }
86
94
  }, inputConfig));
87
95
  };