From 5e66f74ec9f519f6f353af0829e24fe50e026b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 8 Sep 2025 17:56:30 +0800 Subject: [PATCH 1/3] refactor: Upgrade utils and replace useMergedState --- package.json | 2 +- src/Menu.tsx | 41 ++++++++++++++++++----------------------- src/hooks/useUUID.ts | 4 ++-- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index fc9dfb54..85bd150d 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "dependencies": { "@rc-component/motion": "^1.1.4", "@rc-component/trigger": "^3.0.0", - "@rc-component/util": "^1.0.0", + "@rc-component/util": "^1.3.0", "classnames": "2.x", "rc-overflow": "^1.3.1" }, diff --git a/src/Menu.tsx b/src/Menu.tsx index 9c08850e..24cd9c32 100644 --- a/src/Menu.tsx +++ b/src/Menu.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import type { CSSMotionProps } from '@rc-component/motion'; import Overflow from 'rc-overflow'; -import useMergedState from '@rc-component/util/lib/hooks/useMergedState'; +import useControlledState from '@rc-component/util/lib/hooks/useControlledState'; import isEqual from '@rc-component/util/lib/isEqual'; import warning from '@rc-component/util/lib/warning'; import * as React from 'react'; @@ -273,10 +273,8 @@ const Menu = React.forwardRef((props, ref) => { } // ========================= Open ========================= - const [mergedOpenKeys, setMergedOpenKeys] = useMergedState(defaultOpenKeys, { - value: openKeys, - postState: keys => keys || EMPTY_LIST, - }); + const [innerOpenKeys, setMergedOpenKeys] = useControlledState(defaultOpenKeys, openKeys); + const mergedOpenKeys = innerOpenKeys || EMPTY_LIST; // React 18 will merge mouse event which means we open key will not sync // ref: https://github.com/ant-design/ant-design/issues/38818 @@ -376,11 +374,9 @@ const Menu = React.forwardRef((props, ref) => { }, [lastVisibleIndex, allVisible]); // ======================== Active ======================== - const [mergedActiveKey, setMergedActiveKey] = useMergedState( + const [mergedActiveKey, setMergedActiveKey] = useControlledState( activeKey || ((defaultActiveFirst && childList[0]?.key) as string), - { - value: activeKey, - }, + activeKey, ); const onActive = useMemoCallback((key: string) => { @@ -423,22 +419,21 @@ const Menu = React.forwardRef((props, ref) => { // ======================== Select ======================== // >>>>> Select keys - const [mergedSelectKeys, setMergedSelectKeys] = useMergedState(defaultSelectedKeys || [], { - value: selectedKeys, - - // Legacy convert key to array - postState: keys => { - if (Array.isArray(keys)) { - return keys; - } + const [internalSelectKeys, setMergedSelectKeys] = useControlledState( + defaultSelectedKeys || [], + selectedKeys, + ); + const mergedSelectKeys = React.useMemo(() => { + if (Array.isArray(internalSelectKeys)) { + return internalSelectKeys; + } - if (keys === null || keys === undefined) { - return EMPTY_LIST; - } + if (internalSelectKeys === null || internalSelectKeys === undefined) { + return EMPTY_LIST; + } - return [keys]; - }, - }); + return [internalSelectKeys]; + }, [internalSelectKeys]); // >>>>> Trigger select const triggerSelection = (info: MenuInfo) => { diff --git a/src/hooks/useUUID.ts b/src/hooks/useUUID.ts index a939de82..f143067e 100644 --- a/src/hooks/useUUID.ts +++ b/src/hooks/useUUID.ts @@ -1,12 +1,12 @@ import * as React from 'react'; -import useMergedState from '@rc-component/util/lib/hooks/useMergedState'; +import useControlledState from '@rc-component/util/lib/hooks/useControlledState'; const uniquePrefix = Math.random().toFixed(5).toString().slice(2); let internalId = 0; export default function useUUID(id?: string) { - const [uuid, setUUID] = useMergedState(id, { value: id }); + const [uuid, setUUID] = useControlledState(id, id); React.useEffect(() => { internalId += 1; From 2e327fd5dd31b7a8b6b5affc72b02835b03ea0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 9 Sep 2025 16:05:14 +0800 Subject: [PATCH 2/3] refactor: useUUID replace useId --- src/Menu.tsx | 4 +- src/hooks/useUUID.ts | 18 ---- tests/__snapshots__/Keyboard.spec.tsx.snap | 8 +- tests/__snapshots__/Menu.spec.tsx.snap | 102 +++++++++---------- tests/__snapshots__/MenuItem.spec.tsx.snap | 24 ++--- tests/__snapshots__/Options.spec.tsx.snap | 10 +- tests/__snapshots__/Responsive.spec.tsx.snap | 8 +- tests/__snapshots__/SubMenu.spec.tsx.snap | 20 ++-- 8 files changed, 88 insertions(+), 106 deletions(-) delete mode 100644 src/hooks/useUUID.ts diff --git a/src/Menu.tsx b/src/Menu.tsx index 24cd9c32..2bca5add 100644 --- a/src/Menu.tsx +++ b/src/Menu.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import type { CSSMotionProps } from '@rc-component/motion'; import Overflow from 'rc-overflow'; import useControlledState from '@rc-component/util/lib/hooks/useControlledState'; +import useId from '@rc-component/util/lib/hooks/useId'; import isEqual from '@rc-component/util/lib/isEqual'; import warning from '@rc-component/util/lib/warning'; import * as React from 'react'; @@ -14,7 +15,6 @@ import PrivateContext from './context/PrivateContext'; import { getFocusableElements, refreshElements, useAccessibility } from './hooks/useAccessibility'; import useKeyRecords, { OVERFLOW_KEY } from './hooks/useKeyRecords'; import useMemoCallback from './hooks/useMemoCallback'; -import useUUID from './hooks/useUUID'; import type { BuiltinPlacements, Components, @@ -260,7 +260,7 @@ const Menu = React.forwardRef((props, ref) => { const containerRef = React.useRef(); - const uuid = useUUID(id); + const uuid = useId(id ? `rc-menu-uuid-${id}` : 'rc-menu-uuid'); const isRtl = direction === 'rtl'; diff --git a/src/hooks/useUUID.ts b/src/hooks/useUUID.ts deleted file mode 100644 index f143067e..00000000 --- a/src/hooks/useUUID.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -import useControlledState from '@rc-component/util/lib/hooks/useControlledState'; - -const uniquePrefix = Math.random().toFixed(5).toString().slice(2); - -let internalId = 0; - -export default function useUUID(id?: string) { - const [uuid, setUUID] = useControlledState(id, id); - - React.useEffect(() => { - internalId += 1; - const newId = process.env.NODE_ENV === 'test' ? 'test' : `${uniquePrefix}-${internalId}`; - setUUID(`rc-menu-uuid-${newId}`); - }, []); - - return uuid; -} diff --git a/tests/__snapshots__/Keyboard.spec.tsx.snap b/tests/__snapshots__/Keyboard.spec.tsx.snap index 26f47c1d..1230f1be 100644 --- a/tests/__snapshots__/Keyboard.spec.tsx.snap +++ b/tests/__snapshots__/Keyboard.spec.tsx.snap @@ -13,11 +13,11 @@ HTMLCollection [ role="none" >