React+Material-UI菜单点击报错:Type 'void'无法赋值给点击事件类型
嘿,我一眼就看出问题所在啦!你遇到的这个Type 'void' is not assignable to type '(event: MouseEvent<HTMLDivElement, MouseEvent>) => void'错误,核心原因是你直接执行了函数,而不是给onClick传递一个函数引用。
错误原因拆解
你现在写的是:
<div onClick={handleClose(languages[0])}>{languages[0]}</div>
这里handleClose(languages[0])会在组件渲染时立即执行,而handleClose内部只调用了setLanguage,没有返回任何值(也就是返回void)。但onClick需要的是一个能接收鼠标事件参数的函数类型,所以TypeScript直接报错了。
两种修复方案
方案1:用箭头函数包裹(最简单直接)
把直接调用的函数改成箭头函数,这样onClick拿到的是一个待执行的函数,只有点击时才会触发handleClose:
<div onClick={() => handleClose(languages[0])}>{languages[0]}</div> <div onClick={() => handleClose(languages[1])}>{languages[1]}</div> <div onClick={() => handleClose(languages[2])}>{languages[2]}</div>
方案2:让handleClose返回一个函数(更灵活,适合需要用到event的场景)
修改handleClose的结构,让它先接收语言参数,再返回一个处理事件的函数:
const handleClose = (word: string) => { return (event: React.MouseEvent<HTMLDivElement>) => { // 如果需要用到event对象,比如阻止冒泡,就可以在这里用 // event.stopPropagation(); setLanguage(word); }; };
这样你原来的onClick={handleClose(languages[0])}写法就完全合法了,因为handleClose(languages[0])会返回一个符合要求的事件处理函数。
额外优化:用Material-UI的MenuItem组件(更规范)
其实Material-UI的Menu组件搭配MenuItem使用才是标准做法,MenuItem内置了点击反馈、菜单关闭逻辑等,比自己写div更省心。另外你原来的Menu的onClose也有问题(因为它需要的是关闭菜单的逻辑,不是设置语言的逻辑),我们可以把逻辑拆分得更清晰:
import Button from '@material-ui/core/Button'; import Menu from '@material-ui/core/Menu'; import MenuItem from '@material-ui/core/MenuItem'; import React, { useState } from 'react'; // 别忘了导入useState export default function SimpleMenu() { const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); const [language, setLanguage] = useState('English'); const languages = ['English', 'Chinese ( Simplified )', 'Chinese ( Traditional )']; const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { setAnchorEl(event.currentTarget); }; // 单独处理菜单关闭 const handleMenuClose = () => { setAnchorEl(null); }; // 处理语言选择,同时关闭菜单 const handleLanguageSelect = (word: string) => { setLanguage(word); handleMenuClose(); }; return ( <div> <Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}> {language} {/* 这里可以显示当前选中的语言,更友好 */} </Button> <Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleMenuClose} > {languages.map((lang) => ( <MenuItem key={lang} onClick={() => handleLanguageSelect(lang)}> {lang} </MenuItem> ))} </Menu> </div> ); }
这里我还加了用map渲染菜单选项的写法,避免重复代码,同时把按钮文字改成当前选中的语言,体验更好。
内容的提问来源于stack exchange,提问作者da coconut

