React跨组件打开模态框:解决Invalid hook call错误
解决React模态框触发的Invalid Hook Call错误
问题根源
你在AlbumCard的按钮onClick事件里直接调用ModalDialog组件函数,这会导致组件内部的useState钩子在事件回调中执行——而React钩子必须严格在函数组件的顶层作用域调用,不能嵌套在事件处理、循环这类逻辑里,这直接违反了钩子规则,所以抛出错误。
正确的做法是把模态框的显示状态控制权交给父组件AlbumCard,通过props将状态和关闭方法传递给ModalDialog,由父组件的按钮来触发状态更新。
修正后的代码
1. ModalDialog.js(改为受控组件)
import React from "react"; import Button from 'react-bootstrap/Button'; import Modal from 'react-bootstrap/Modal'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import {faTrash } from "@fortawesome/free-solid-svg-icons"; // 接收父组件传递的show状态和关闭方法 function ModalDialog({ show, handleClose }) { return ( <> <Modal show={show} onHide={handleClose} backdrop="static" keyboard={false} > <Modal.Header closeButton> <div className="container "> <div className="h2 d-flex justify-content-start">Album Name</div> </div> </Modal.Header> <Modal.Body> <div className="container justify-content-center"> <table className="mt-3 table table-hover table-light text-center"> <thead> <tr> <th scope="col">Album Name</th> <th scope="col">Release Date</th> <th scope="col"></th> </tr> </thead> <tbody> <tr id="1"> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faTrash} /> </td> </tr> </tbody> </table> </div> </Modal.Body> <Modal.Footer> <div className="container d-flex justify-content-center"> <Button variant="primary ms-2" onClick={handleClose}> Add New Song </Button> </div> </Modal.Footer> </Modal> </> ); } export default ModalDialog;
2. AlbumCard.js(管理模态框状态并渲染组件)
import { useState } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import {faCompactDisc,faCirclePlus,faSquareArrowUpRight } from "@fortawesome/free-solid-svg-icons"; import ModalDialog from '../Components/ModalDialog' import '../Styles/AlbumCard.css'; const AlbumCard = () => { // 新增状态管理模态框的显示/隐藏 const [showModal, setShowModal] = useState(false); const handleOpenModal = () => setShowModal(true); const handleCloseModal = () => setShowModal(false); return ( <div className="mt-4 container width: 92%"> <div className="container"> <div className="row"> <div className="col"> <div className="h3 d-flex justify-content-start"> <FontAwesomeIcon icon={faCompactDisc} /> <div className="ms-3"> Album List </div> </div> </div> <div className="col"> <div className="h3 d-flex justify-content-end"> <div className=""> {/* 点击时调用打开模态框的方法 */} <button type="button" className="btn btn-info bg-dark text-light" onClick={handleOpenModal}> Add <span className="ms-2"> <FontAwesomeIcon icon={faCirclePlus} /> </span> </button> </div> </div> </div> </div> </div> {/* 渲染模态框组件,传递状态和关闭方法 */} <ModalDialog show={showModal} handleClose={handleCloseModal} /> <table className="mt-3 table table-hover table-light"> <thead> <tr id="1"> <th scope="col">Album Name</th> <th scope="col">Release Date</th> <th scope="col"></th> </tr> </thead> <tbody> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> <tr> <td>Album 1</td> <td>2023-06-27</td> <td> <FontAwesomeIcon icon={faSquareArrowUpRight} /> </td> </tr> </tbody> </table> </div> ); }; export default AlbumCard;
额外说明
- 把
class改为className:React中JSX里的类名必须用className,避免和JavaScript的关键字冲突。 - 状态提升:将模态框的显示状态放在父组件,是React中控制子组件显示/隐藏的标准做法,确保状态逻辑集中且可维护。
内容的提问来源于stack exchange,提问作者aGuy
相关产品推荐
相关产品推荐

