请问为何antd/dropdown的menu属性传递调用函数生成items会报错呢?

我有如下的代码使用antd/Dropdown组件:

image.png

 const items: MenuProps["items"] = [
    {
      label: <div  onClick={() => {
        console.log('收藏: ')
      }}>收藏</div>,
      key: '0',
    },
    {
      label: <a>2nd menu item</a>,
      key: '1',
    },
    {
      type: 'divider',
    },
    {
      label: '3rd menu item',
      key: '3',
    },
  ];

...
 <Dropdown 
    key={nodeData.key} 
    menu={{ items }} 
    trigger={['click']} 
    open={isOpenObj[nodeData.key]}
    onOpenChange={()=>{
      updateIsOpenObj(nodeData.key, false)
    }}
    >
      <a onClick={(e) => {
        e.preventDefault()

      }}>
        <Space>
        </Space>
      </a>
    </Dropdown>

这样使用是没有问题的:
调用打开dropdown:
image.png

但是我修改为此代码(使用genDropdownItems()生成items,而不直接传递items,就会报错):

// 生成dropdown的key
  const genDropdownItems = (key: string | number): MenuProps["items"]  => {
    const items: MenuProps["items"] = [
      {
        label: <div  onClick={() => {
          console.log('收藏: ', key)
        }}>收藏</div>,
        key: '0',
      },
      {
        label: <a>2nd menu item</a>,
        key: '1',
      },
      {
        type: 'divider',
      },
      {
        label: '3rd menu item',
        key: '3',
      },
    ];
    return items 
  }


<Dropdown 
    key={nodeData.key} 
    //menu={{ items }} 
    menu={genDropdownItems(nodeData.key)}
    trigger={['click']} 
    open={isOpenObj[nodeData.key]}
    onOpenChange={()=>{
      updateIsOpenObj(nodeData.key, false)
    }}
    >
      <a onClick={(e) => {
        e.preventDefault()

      }}>
        <Space>
        </Space>
      </a>
    </Dropdown>

报错结果:
image.png

Unhandled Runtime Error
Error: React.Children.only expected to receive a single React element child.

Call Stack
Object.onlyChild [as only]

...

at PathnameContextProviderAdapter (webpack-internal:///../node_modules/.pnpm/registry.npmmirror.com+next@13.5.2_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/shared/lib/router/adapters.js:79:11)
    at ErrorBoundary (webpack-internal:///../node_modules/.pnpm/registry.npmmirror.com+next@13.5.2_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:2:5389)
    at ReactDevOverlay (webpack-internal:///../node_modules/.pnpm/registry.npmmirror.com+next@13.5.2_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:2:7785)
    at Container (webpack-internal:///../node_modules/.pnpm/registry.npmmirror.com+next@13.5.2_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/index.js:78:1)
    at AppContainer (webpack-internal:///../node_modules/.pnpm/registry.npmmirror.com+next@13.5.2_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/index.js:182:11)
    at Root (webpack-internal:///../node_modules/.pnpm/registry.npmmirror.com+next@13.5.2_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/index.js:396:11)

React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.
阅读 1.5k
avatarAI BotBETA

看起来你的问题是在尝试将函数genDropdownItems()作为menu属性值传入Dropdown组件时遇到了错误。从React的角度来看,这个错误可能是因为genDropdownItems()函数没有返回一个有效的React元素。

在Next.js和React中,一个组件只能有一个子元素,这是由React的React.Children.only()函数保证的。这个函数期望只有一个子元素,如果有多于一个的子元素,它就会抛出错误。在你的代码中,genDropdownItems()函数返回了一个数组,而这个数组被作为menu属性的值传递给了Dropdown组件,这就导致了错误的发生。

解决这个问题的一种方式是修改genDropdownItems()函数,让它返回一个有效的React元素,而不是一个数组。例如,你可以将每个菜单项封装在一个<MenuItem />组件中,并返回一个包含所有这些<MenuItem />组件的数组。

下面是一个可能的解决方案:

const MenuItem = ({ label, onClick }) => (
  <div onClick={onClick}>
    {label}
  </div>
);

const genDropdownItems = (key: string | number): MenuProps["items"] => {
  const items: MenuProps["items"] = [
    {
      label: <MenuItem onClick={() => { console.log('收藏: ', key); }}>收藏</MenuItem>,
      key: '0',
    },
    {
      label: <MenuItem>2nd menu item</MenuItem>,
      key: '1',
    },
    {
      type: 'divider',
    },
    {
      label: '3rd menu item',
      key: '3',
    },
  ];
  return items; 
}

//...你的其他代码...
<Dropdown 
    key={nodeData.key} 
    menu={genDropdownItems(nodeData.key)}
    trigger={['click']} 
    open={isOpenObj[nodeData.key]}
    onOpenChange={()=>{
      updateIsOpenObj(nodeData.key, false)
    }}
    >
      <a onClick={(e) => {
        e.preventDefault()
      }}>
        <Space>
        </Space>
      </a>
    </Dropdown>

在这个解决方案中,我们创建了一个新的MenuItem组件来封装每个菜单项的显示和点击行为,然后在genDropdownItems()函数中返回包含这些MenuItem组件的数组。这样,Dropdown组件就可以正确地接收并渲染这些菜单项了。

2 个回答

你少了对{}吧

const genDropdownItems = (key: string | number): MenuProps["items"]  => {
    const items: MenuProps["items"] = [
      {
        label: <div  onClick={() => {
          console.log('收藏: ', key)
        }}>收藏</div>,
        key: '0',
      },
      {
        label: <a>2nd menu item</a>,
        key: '1',
      },
      {
        type: 'divider',
      },
      {
        label: '3rd menu item',
        key: '3',
      },
    ];
    //return items
    return { items } 
  }

函数调用不是这样写的么 menu={()=>genDropdownItems(nodeData.key)}

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Microsoft
子站问答
访问
宣传栏