Skip to content

Commit 5efe1bc

Browse files
committed
refactor: migrate legacy menu item to fc
1 parent 16485d7 commit 5efe1bc

2 files changed

Lines changed: 67 additions & 35 deletions

File tree

src/MenuItem.tsx

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,40 +34,71 @@ export interface MenuItemProps
3434
itemData?: ItemData;
3535
}
3636

37-
// Since Menu event provide the `info.item` which point to the MenuItem node instance.
38-
// We have to use class component here.
39-
// This should be removed from doc & api in future.
40-
class LegacyMenuItem extends React.Component<any> {
41-
render() {
42-
const { title, attribute, elementRef, ...restProps } = this.props;
43-
44-
// Here the props are eventually passed to the DOM element.
45-
// React does not recognize non-standard attributes.
46-
// Therefore, remove the props that is not used here.
47-
// ref: https://github.com/ant-design/ant-design/issues/41395
48-
const passedProps = omit(restProps, [
49-
'eventKey',
50-
'popupClassName',
51-
'popupOffset',
52-
'onTitleClick',
53-
]);
54-
warning(!attribute, '`attribute` of Menu.Item is deprecated. Please pass attribute directly.');
55-
56-
return (
57-
<Overflow.Item
58-
{...attribute}
59-
title={typeof title === 'string' ? title : undefined}
60-
{...passedProps}
61-
ref={elementRef}
62-
/>
63-
);
64-
}
65-
}
37+
type LegacyMenuItemProps = Omit<React.ComponentProps<typeof Overflow.Item>, 'title' | 'ref'> & {
38+
title?: React.ReactNode;
39+
attribute?: Record<string, string>;
40+
elementRef?: React.Ref<HTMLLIElement>;
41+
eventKey?: string;
42+
popupClassName?: string;
43+
popupOffset?: number[];
44+
onTitleClick?: () => void;
45+
};
46+
47+
type LegacyMenuItemHandle = {
48+
props: LegacyMenuItemProps;
49+
element: HTMLLIElement | null;
50+
};
51+
52+
// Keep exposing a legacy-compatible handle for deprecated `info.item`.
53+
// This should be removed together with the deprecated API in future.
54+
const LegacyMenuItem = React.forwardRef<LegacyMenuItemHandle, LegacyMenuItemProps>((props, ref) => {
55+
const { title, attribute, elementRef, ...restProps } = props;
56+
const propsRef = React.useRef(props);
57+
const domRef = React.useRef<HTMLLIElement>(null);
58+
59+
propsRef.current = props;
60+
61+
React.useImperativeHandle(
62+
ref,
63+
() => ({
64+
get props() {
65+
return propsRef.current;
66+
},
67+
get element() {
68+
return domRef.current;
69+
},
70+
}),
71+
[],
72+
);
73+
74+
// Here the props are eventually passed to the DOM element.
75+
// React does not recognize non-standard attributes.
76+
// Therefore, remove the props that is not used here.
77+
// ref: https://github.com/ant-design/ant-design/issues/41395
78+
const passedProps = omit(restProps, [
79+
'eventKey',
80+
'popupClassName',
81+
'popupOffset',
82+
'onTitleClick',
83+
]);
84+
const mergedRef = useComposeRef(elementRef, domRef);
85+
86+
warning(!attribute, '`attribute` of Menu.Item is deprecated. Please pass attribute directly.');
87+
88+
return (
89+
<Overflow.Item
90+
{...attribute}
91+
title={typeof title === 'string' ? title : undefined}
92+
{...passedProps}
93+
ref={mergedRef}
94+
/>
95+
);
96+
});
6697

6798
/**
6899
* Real Menu Item component
69100
*/
70-
const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<HTMLElement>) => {
101+
const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<HTMLLIElement>) => {
71102
const {
72103
style,
73104
className,
@@ -117,7 +148,7 @@ const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<
117148

118149
const itemCls = `${prefixCls}-item`;
119150

120-
const legacyMenuItemRef = React.useRef<any>();
151+
const legacyMenuItemRef = React.useRef<LegacyMenuItemHandle | null>(null);
121152
const elementRef = React.useRef<HTMLLIElement>();
122153
const mergedDisabled = contextDisabled || disabled;
123154

@@ -147,7 +178,7 @@ const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<
147178
key: eventKey,
148179
// Note: For legacy code is reversed which not like other antd component
149180
keyPath: [...connectedKeys].reverse(),
150-
item: legacyMenuItemRef.current,
181+
item: legacyMenuItemRef.current as unknown as React.ReactInstance,
151182
domEvent: e,
152183
itemData: propsItemData || itemData,
153184
};
@@ -214,6 +245,7 @@ const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<
214245
<LegacyMenuItem
215246
ref={legacyMenuItemRef}
216247
elementRef={mergedEleRef}
248+
eventKey={eventKey}
217249
role={role === null ? 'none' : role || 'menuitem'}
218250
tabIndex={disabled ? null : -1}
219251
data-menu-id={overflowDisabled && domDataId ? null : domDataId}
@@ -257,7 +289,7 @@ const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<
257289
return renderNode;
258290
});
259291

260-
function MenuItem(props: MenuItemProps, ref: React.Ref<HTMLElement>): React.ReactElement {
292+
function MenuItem(props: MenuItemProps, ref: React.Ref<HTMLLIElement>): React.ReactElement {
261293
const { eventKey } = props;
262294

263295
// ==================== Record KeyPath ====================

src/utils/warnUtil.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { warning } from '@rc-component/util';
22

33
/**
4-
* `onClick` event return `info.item` which point to react node directly.
5-
* We should warning this since it will not work on FC.
4+
* `onClick` still exposes deprecated `info.item` for backward compatibility.
5+
* Keep warning since function components no longer provide a React node instance.
66
*/
77
export function warnItemProp<T extends { item: React.ReactInstance }>({ item, ...restInfo }: T): T {
88
Object.defineProperty(restInfo, 'item', {

0 commit comments

Comments
 (0)