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

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;

关键修改点说明

  1. 状态提升:在TutorCard中新增modalOpen状态,统一控制模态框显示/隐藏,符合React状态管理逻辑。
  2. 事件处理修正:按钮onClick改为调用状态更新函数handleOpenModal,不再直接调用组件函数。
  3. 组件职责拆分:DetailedModal变为受控组件,通过接收open和onClose props控制显示,同时接收tutorInfo展示对应导师的详情。
  4. 遵守Hook规则:所有Hook均在组件顶层调用,未违反Hook使用规范。

额外建议

你的package.json中react-scripts版本为^2.1.3,版本过于老旧,建议升级至最新稳定版(当前为v5.x),避免旧版本带来的兼容性问题。升级命令:

npm install react-scripts@latest

内容的提问来源于stack exchange,提问作者BaMdan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:37:03