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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 12:37:11