From 50b7e6e90c55904d1fcedfb29193e930ff14fcf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 9 Sep 2025 14:51:35 +0800 Subject: [PATCH 1/5] refactor: Upgrade utils and replace useMergedState --- package.json | 2 +- src/PickerInput/RangePicker.tsx | 6 ++---- src/PickerInput/SinglePicker.tsx | 6 ++---- src/PickerInput/hooks/useRangePickerValue.ts | 15 ++++++++------- src/PickerInput/hooks/useRangeValue.ts | 4 ++-- src/PickerPanel/index.tsx | 17 +++++------------ 6 files changed, 20 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index f4dfc4d82..16020275b 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "dependencies": { "@rc-component/resize-observer": "^1.0.0", "@rc-component/trigger": "^3.0.0", - "@rc-component/util": "^1.2.1", + "@rc-component/util": "^1.3.0", "classnames": "^2.2.1", "rc-overflow": "^1.3.2" }, diff --git a/src/PickerInput/RangePicker.tsx b/src/PickerInput/RangePicker.tsx index 7a0b57839..5e6fee2cc 100644 --- a/src/PickerInput/RangePicker.tsx +++ b/src/PickerInput/RangePicker.tsx @@ -1,4 +1,4 @@ -import { useEvent, useMergedState } from '@rc-component/util'; +import { useEvent, useControlledState } from '@rc-component/util'; import cls from 'classnames'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import omit from '@rc-component/util/lib/omit'; @@ -310,9 +310,7 @@ function RangePicker( }, [showTime, activeIndex, calendarValue, activeIndexList]); // ========================= Mode ========================= - const [modes, setModes] = useMergedState<[PanelMode, PanelMode]>([picker, picker], { - value: mode, - }); + const [modes, setModes] = useControlledState<[PanelMode, PanelMode]>([picker, picker], mode); const mergedMode = modes[activeIndex] || picker; diff --git a/src/PickerInput/SinglePicker.tsx b/src/PickerInput/SinglePicker.tsx index c773fbd09..1e40be7d9 100644 --- a/src/PickerInput/SinglePicker.tsx +++ b/src/PickerInput/SinglePicker.tsx @@ -1,4 +1,4 @@ -import { useEvent, useMergedState } from '@rc-component/util'; +import { useEvent, useControlledState } from '@rc-component/util'; import cls from 'classnames'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import omit from '@rc-component/util/lib/omit'; @@ -259,9 +259,7 @@ function Picker( }; // ========================= Mode ========================= - const [mergedMode, setMode] = useMergedState(picker, { - value: mode, - }); + const [mergedMode, setMode] = useControlledState(picker, mode); /** Extends from `mergedMode` to patch `datetime` mode */ const internalMode: InternalMode = mergedMode === 'date' && showTime ? 'datetime' : mergedMode; diff --git a/src/PickerInput/hooks/useRangePickerValue.ts b/src/PickerInput/hooks/useRangePickerValue.ts index f5181f402..4a662b276 100644 --- a/src/PickerInput/hooks/useRangePickerValue.ts +++ b/src/PickerInput/hooks/useRangePickerValue.ts @@ -1,4 +1,4 @@ -import { useMergedState } from '@rc-component/util'; +import { useControlledState } from '@rc-component/util'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import * as React from 'react'; import type { GenerateConfig } from '../../generate'; @@ -72,14 +72,15 @@ export default function useRangePickerValue getDefaultPickerValue(0), - { value: startPickerValue }, + const [mergedStartPickerValue, setStartPickerValue] = useControlledState( + getDefaultPickerValue(0), + startPickerValue, ); - const [mergedEndPickerValue, setEndPickerValue] = useMergedState(() => getDefaultPickerValue(1), { - value: endPickerValue, - }); + const [mergedEndPickerValue, setEndPickerValue] = useControlledState( + getDefaultPickerValue(1), + endPickerValue, + ); // Current PickerValue const currentPickerValue = React.useMemo(() => { diff --git a/src/PickerInput/hooks/useRangeValue.ts b/src/PickerInput/hooks/useRangeValue.ts index 89b139d84..f7267f9d5 100644 --- a/src/PickerInput/hooks/useRangeValue.ts +++ b/src/PickerInput/hooks/useRangeValue.ts @@ -1,4 +1,4 @@ -import { useEvent, useMergedState } from '@rc-component/util'; +import { useEvent, useControlledState } from '@rc-component/util'; import * as React from 'react'; import type { GenerateConfig } from '../../generate'; import useSyncState from '../../hooks/useSyncState'; @@ -117,7 +117,7 @@ export function useInnerValue void, ) { // This is the root value which will sync with controlled or uncontrolled value - const [innerValue, setInnerValue] = useMergedState(defaultValue, { value }); + const [innerValue, setInnerValue] = useControlledState(defaultValue, value); const mergedValue = innerValue || (EMPTY_VALUE as ValueType); // ========================= Inner Values ========================= diff --git a/src/PickerPanel/index.tsx b/src/PickerPanel/index.tsx index 6dab2f3fb..e9ed18126 100644 --- a/src/PickerPanel/index.tsx +++ b/src/PickerPanel/index.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import { useEvent, useMergedState, warning } from '@rc-component/util'; +import { useEvent, useControlledState, warning } from '@rc-component/util'; import * as React from 'react'; import useLocale from '../hooks/useLocale'; import { fillShowTimeConfig, getTimeProps } from '../hooks/useTimeConfig'; @@ -227,10 +227,7 @@ function PickerPanel( const now = generateConfig.getNow(); // ========================== Mode ========================== - const [mergedMode, setMergedMode] = useMergedState(picker, { - value: mode, - postState: (val) => val || 'date', - }); + const [mergedMode, setMergedMode] = useControlledState(picker || 'date', mode); const internalMode: InternalMode = mergedMode === 'date' && mergedShowTime ? 'datetime' : mergedMode; @@ -241,9 +238,7 @@ function PickerPanel( // ========================= Value ========================== // >>> Real value // Interactive with `onChange` event which only trigger when the `mode` is `picker` - const [innerValue, setMergedValue] = useMergedState(defaultValue, { - value, - }); + const [innerValue, setMergedValue] = useControlledState(defaultValue, value); const mergedValue = React.useMemo(() => { // Clean up `[null]` @@ -282,11 +277,9 @@ function PickerPanel( // >>> PickerValue // PickerValue is used to control the current displaying panel - const [mergedPickerValue, setInternalPickerValue] = useMergedState( + const [mergedPickerValue, setInternalPickerValue] = useControlledState( defaultPickerValue || mergedValue[0] || now, - { - value: pickerValue, - }, + pickerValue, ); React.useEffect(() => { From 8d2fe35cb420fd84e816c53c96cd8f194ca7650c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 9 Sep 2025 15:03:02 +0800 Subject: [PATCH 2/5] feat: defaultvalue --- src/PickerInput/hooks/useRangePickerValue.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PickerInput/hooks/useRangePickerValue.ts b/src/PickerInput/hooks/useRangePickerValue.ts index 4a662b276..be21e528b 100644 --- a/src/PickerInput/hooks/useRangePickerValue.ts +++ b/src/PickerInput/hooks/useRangePickerValue.ts @@ -73,12 +73,12 @@ export default function useRangePickerValue getDefaultPickerValue(0), startPickerValue, ); const [mergedEndPickerValue, setEndPickerValue] = useControlledState( - getDefaultPickerValue(1), + () => getDefaultPickerValue(1), endPickerValue, ); From a940494b600cc0788fcd54bf25ff76399745cc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 9 Sep 2025 16:38:03 +0800 Subject: [PATCH 3/5] feat: replace useMergedState in useDelayState --- src/PickerInput/hooks/useDelayState.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PickerInput/hooks/useDelayState.ts b/src/PickerInput/hooks/useDelayState.ts index 6be0c127d..606f75bd3 100644 --- a/src/PickerInput/hooks/useDelayState.ts +++ b/src/PickerInput/hooks/useDelayState.ts @@ -1,4 +1,4 @@ -import { useEvent, useMergedState } from '@rc-component/util'; +import { useEvent, useControlledState } from '@rc-component/util'; import raf from '@rc-component/util/lib/raf'; import React from 'react'; @@ -11,7 +11,7 @@ export default function useDelayState( defaultValue?: T, onChange?: (next: T) => void, ): [state: T, setState: (nextState: T, immediately?: boolean) => void] { - const [state, setState] = useMergedState(defaultValue, { value }); + const [state, setState] = useControlledState(defaultValue, value); const nextValueRef = React.useRef(value); From 82d0a1a1f2ed027a39778846545823922decd469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 10 Sep 2025 10:58:43 +0800 Subject: [PATCH 4/5] chore: adjust logic --- src/PickerInput/hooks/useDelayState.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/PickerInput/hooks/useDelayState.ts b/src/PickerInput/hooks/useDelayState.ts index 606f75bd3..b36a5f78f 100644 --- a/src/PickerInput/hooks/useDelayState.ts +++ b/src/PickerInput/hooks/useDelayState.ts @@ -1,4 +1,4 @@ -import { useEvent, useControlledState } from '@rc-component/util'; +import { useEvent, useControlledState, useMergedState } from '@rc-component/util'; import raf from '@rc-component/util/lib/raf'; import React from 'react'; @@ -13,6 +13,14 @@ export default function useDelayState( ): [state: T, setState: (nextState: T, immediately?: boolean) => void] { const [state, setState] = useControlledState(defaultValue, value); + // Need force update to ensure React re-render + const [, forceUpdate] = React.useState({}); + + const triggerUpdate = useEvent((nextState: T) => { + setState(nextState); + forceUpdate({}); + }); + const nextValueRef = React.useRef(value); // ============================= Update ============================= @@ -22,7 +30,7 @@ export default function useDelayState( }; const doUpdate = useEvent(() => { - setState(nextValueRef.current); + triggerUpdate(nextValueRef.current); if (onChange && state !== nextValueRef.current) { onChange(nextValueRef.current); From ae9def7b61b80c29bb77edd46e7f650b905d8c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 10 Sep 2025 10:59:54 +0800 Subject: [PATCH 5/5] chore: clean up --- src/PickerInput/hooks/useDelayState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PickerInput/hooks/useDelayState.ts b/src/PickerInput/hooks/useDelayState.ts index b36a5f78f..0904bcf0a 100644 --- a/src/PickerInput/hooks/useDelayState.ts +++ b/src/PickerInput/hooks/useDelayState.ts @@ -1,4 +1,4 @@ -import { useEvent, useControlledState, useMergedState } from '@rc-component/util'; +import { useEvent, useControlledState } from '@rc-component/util'; import raf from '@rc-component/util/lib/raf'; import React from 'react';