ReactJS Web应用使用Material UI时触发Invalid Hook Call Error
解决React中点击按钮触发Invalid Hook Call Error的问题
问题根源
你的代码犯了典型的React Hook使用错误:在TutorCard组件的按钮点击事件中,直接将DetailedModal这个带Hook的组件函数当作事件处理函数调用了。
点击"Details"按钮时,React会执行DetailedModal(),而这个函数内部调用了React.useState——根据Hook规则,Hook只能在React组件的顶层作用域调用,不能在事件处理函数、循环、条件判断或嵌套函数里调用,这直接触发了Invalid Hook Call Error。
另外,DetailedModal本身是包含独立按钮和状态的完整组件,但你的需求是点击卡片按钮打开模态框,所以组件结构也需要调整。
修复方案
我们需要把模态框的显示状态提升到TutorCard组件中,让卡片控制模态框的显示/隐藏,同时让DetailedModal接收外部状态和回调作为props,不再自行管理状态。
修改后的完整代码
import React from "react"; import Button from '@mui/material/Button'; import PropTypes from 'prop-types'; import { styled } from '@mui/material/styles'; import Dialog from '@mui/material/Dialog'; import DialogTitle from '@mui/material/DialogTitle'; import DialogContent from '@mui/material/DialogContent'; import DialogActions from '@mui/material/DialogActions'; import IconButton from '@mui/material/IconButton'; import CloseIcon from '@mui/icons-material/Close'; import Typography from '@mui/material/Typography'; function TutorCard({ first_name, last_name, grade, subject }) { // 将模态框状态提升到卡片组件 const [modalOpen, setModalOpen] = React.useState(false); const handleOpenModal = () => { setModalOpen(true); }; const handleCloseModal = () => { setModalOpen(false); }; return ( <div className="md:flex bg-white shadow text-gray-800 my-4 py-4 px-10 rounded-md items-center justify-between hover:bg-gray-300"> <p className="font text-md" style={{ color: 'black', textAlign: "center" }}>{first_name}</p> <p className="font text-md" style={{ color: 'black', textAlign: "center" }}>{last_name}</p> <p style={{ color: 'black', textAlign: "center" }}>{grade}</p> <p style={{ color: 'black', textAlign: "center" }}>{subject}</p> {/* 点击按钮调用状态更新函数,而非组件 */} <Button onClick={handleOpenModal}> Details </Button> {/* 在组件顶层渲染模态框,通过状态控制显示 */} <DetailedModal open={modalOpen} onClose={handleCloseModal} tutorInfo={{ first_name, last_name, grade, subject }} /> </div> ) } const BootstrapDialog = styled(Dialog)(({ theme }) => ({ '& .MuiDialogContent-root': { padding: theme.spacing(2), }, '& .MuiDialogActions-root': { padding: theme.spacing(1), }, })); function BootstrapDialogTitle(props) { const { children, onClose, ...other } = props; return ( <DialogTitle sx={{ m: 0, p: 2 }} {...other}> {children} {onClose ? ( <IconButton aria-label="close" onClick={onClose} sx={{ position: 'absolute', right: 8, top: 8, color: (theme) => theme.palette.grey[500], }} > <CloseIcon /> </IconButton> ) : null} </DialogTitle> ); } BootstrapDialogTitle.propTypes = { children: PropTypes.node, onClose: PropTypes.func.isRequired, }; // 修改DetailedModal,接收外部状态、回调和导师信息 function DetailedModal({ open, onClose, tutorInfo }) { return ( <BootstrapDialog onClose={onClose} aria-labelledby="customized-dialog-title" open={open} > <BootstrapDialogTitle id="customized-dialog-title" onClose={onClose}> {tutorInfo.first_name} {tutorInfo.last_name} 的详情 </BootstrapDialogTitle> <DialogContent dividers> <Typography gutterBottom> 年级: {tutorInfo.grade} </Typography> <Typography gutterBottom> 科目: {tutorInfo.subject} </Typography> <Typography gutterBottom> 其他详情内容可在此添加... </Typography> </DialogContent> <DialogActions> <Button autoFocus onClick={onClose}> 关闭 </Button> </DialogActions> </BootstrapDialog> ); } DetailedModal.propTypes = { open: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, tutorInfo: PropTypes.shape({ first_name: PropTypes.string, last_name: PropTypes.string, grade: PropTypes.string, subject: PropTypes.string, }).isRequired, }; export default TutorCard;
关键修改点说明
- 状态提升:在
TutorCard中新增modalOpen状态,统一控制模态框显示/隐藏,符合React状态管理逻辑。 - 事件处理修正:按钮
onClick改为调用状态更新函数handleOpenModal,不再直接调用组件函数。 - 组件职责拆分:
DetailedModal变为受控组件,通过接收open和onCloseprops控制显示,同时接收tutorInfo展示对应导师的详情。 - 遵守Hook规则:所有Hook均在组件顶层调用,未违反Hook使用规范。
额外建议
你的package.json中react-scripts版本为^2.1.3,版本过于老旧,建议升级至最新稳定版(当前为v5.x),避免旧版本带来的兼容性问题。升级命令:
npm install react-scripts@latest
内容的提问来源于stack exchange,提问作者BaMdan
相关产品推荐
相关产品推荐

