You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Antd 3.5.4抽离UserMenu组件时SubMenu创建失败求助

解决Ant Design 3.5.4中抽离UserMenu组件后的SubMenu报错问题

我之前也遇到过Antd 3.x版本中类似的组件抽离问题,本质原因是Antd的Menu组件只会给直接子节点(Menu.Item/SubMenu)注入必要的上下文props,当你把SubMenu包裹在自定义组件里时,这些props无法传递到内部的SubMenu,导致了isRootMenu和onItemHover的错误。

错误原因分析

Antd 3.x的SubMenu依赖父级Menu通过React.Children遍历子元素时注入的一系列核心props:

  • parentMenu: 指向父级Menu实例,用于判断菜单层级
  • onItemHover: 处理鼠标移入移出的回调函数

当你使用<UserMenu />作为Menu的子组件时,Menu只会把这些props传递给UserMenu本身,而不会自动传递给UserMenu内部的SubMenu,所以SubMenu缺少必要的依赖,抛出错误。

最优解决方案:让UserMenu直接返回Menu子元素

最简单的方法是把UserMenu改成一个返回Menu子元素的纯函数,而不是作为独立组件标签使用,这样返回的SubMenu/Menu.Item会直接成为Menu的子节点,Menu会自动注入所需props。

步骤1:重构UserMenu为纯函数组件

// UserMenu.js
import React from 'react';
import { Menu, Avatar } from 'antd';
import { Link } from 'react-router-dom';

const { SubMenu, Item: MenuItem } = Menu;

export const UserMenu = ({ user, signOutUser, RoutesNames }) => {
  if (!user) return null;

  // 已登录状态:返回SubMenu
  if (user.isSignedIn) {
    return (
      <SubMenu 
        key="usermenu" 
        title={<Avatar>{user.email.charAt(0).toUpperCase()}</Avatar>}
      >
        <MenuItem key="info">{user.email}</MenuItem>
        <MenuItem key="logout">
          <Link onClick={signOutUser}>Log out</Link>
        </MenuItem>
      </SubMenu>
    );
  }

  // 未登录状态:返回登录入口MenuItem
  return (
    <MenuItem key="login">
      <Link to={RoutesNames.LOGIN}>Signin / Register</Link>
    </MenuItem>
  );
};

步骤2:在父组件中调用函数而非使用组件标签

// 父组件
import { UserMenu } from './UserMenu';

// ...

render() {
  const { currentUser, signOutUser } = this.props;
  return (
    <Menu mode="horizontal">
      <Menu.Item key="home">
        <Link to={RoutesNames.HOMEPAGE}>Home</Link>
      </Menu.Item>
      {/* 直接调用函数,返回的元素会成为Menu的直接子节点 */}
      {UserMenu({ 
        user: currentUser, 
        signOutUser, 
        RoutesNames 
      })}
    </Menu>
  );
}

备选方案:手动传递Menu上下文props(不推荐)

如果你坚持要使用<UserMenu />组件标签的形式,可以通过Menu的ref获取实例,手动把所需props传递给内部的SubMenu,但这种方法需要依赖Antd内部实现,容易在版本更新时出问题:

// 父组件
class ParentComponent extends React.Component {
  constructor(props) {
    super(props);
    this.menuRef = React.createRef();
  }

  render() {
    const { currentUser, signOutUser } = this.props;
    return (
      <Menu mode="horizontal" ref={this.menuRef}>
        <Menu.Item key="home">
          <Link to={RoutesNames.HOMEPAGE}>Home</Link>
        </Menu.Item>
        <UserMenu 
          user={currentUser} 
          signOutUser={signOutUser} 
          RoutesNames={RoutesNames}
          // 手动传递Menu的核心props
          parentMenu={this.menuRef.current}
          onItemHover={this.menuRef.current?.handleItemHover}
        />
      </Menu>
    );
  }
}
// UserMenu组件
class UserMenu extends React.Component {
  render() {
    const { user, signOutUser, RoutesNames, parentMenu, onItemHover } = this.props;
    if (user.isSignedIn) {
      return (
        <SubMenu 
          key="usermenu" 
          title={<Avatar>A</Avatar>}
          parentMenu={parentMenu}
          onItemHover={onItemHover}
        >
          <Menu.Item key="info">{user.email}</Menu.Item>
          <Menu.Item key="logout">
            <Link onClick={signOutUser}>Log out</Link>
          </Menu.Item>
        </SubMenu>
      );
    } else {
      return (
        <Menu.Item key="login">
          <Link to={RoutesNames.LOGIN}>Signin / Register</Link>
        </Menu.Item>
      );
    }
  }
}

验证效果

第一种方案实施后,SubMenu会正确获取Menu注入的所有props,鼠标移入移出时的onItemHover错误也会消失,完全恢复之前在同一文件中的正常运行效果。

内容的提问来源于stack exchange,提问作者ARNAUD COSSON -CAMPUS-

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:49:25