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

UseRef无法选中DOM元素,点击Card编辑按钮无法唤起Bootstrap Modal

解决Bootstrap Modal ref为空及唤起问题

问题根源

你当前的代码存在几个关键问题:

  • ref传递方式错误:Modal组件用了forwardRef接收ref,但你在App里是通过btn={btn}作为普通props传递的,forwardRef的第二个参数是专门接收ref的通道,不是props,所以导致ref根本没绑定到按钮上。
  • setState异步特性:你在Add函数里刚调用Setli就立刻console.log(btn.current),此时新的Modal组件还没完成渲染,ref自然是空的。
  • 固定Modal ID冲突:所有Modal都用同一个id="staticBackdrop",多个Modal共存时会导致无法分别唤起。

修正方案

1. 先解决ref传递的基础问题

Modal组件用了forwardRef,所以App里必须通过ref属性传递,不能用props:

// App.js的Add函数里修改Modal调用方式
function Add(){
  Setli(li=>[...li,<Model ref={btn}/>])
}

然后要等组件渲染完成再访问ref,用useEffect监听数组变化:

// App.js里新增useEffect
useEffect(() => {
  if (li.length > 0) {
    console.log(btn.current); // 此时ref已绑定
  }
}, [li]);

2. 更优方案:直接用Bootstrap JS API控制Modal(无需依赖触发按钮)

没必要隐藏触发按钮绕弯子,直接用Bootstrap的Modal JS实例来控制显示/隐藏,更灵活可靠:

修改Modal.js
import React, { useRef, useEffect, forwardRef, useImperativeHandle } from "react";
import { Modal as BootstrapModal } from 'bootstrap';

const Model = forwardRef((props, ref) => {
  const modalDomRef = useRef(null);
  let bootstrapModalInstance = null;

  // 初始化Bootstrap Modal实例
  useEffect(() => {
    bootstrapModalInstance = new BootstrapModal(modalDomRef.current);
    return () => bootstrapModalInstance.dispose(); // 组件卸载时清理
  }, []);

  // 向父组件暴露唤起Modal的方法
  useImperativeHandle(ref, () => ({
    show: () => bootstrapModalInstance.show(),
    hide: () => bootstrapModalInstance.hide()
  }));

  return (
    <div
      className="modal fade"
      ref={modalDomRef}
      data-bs-backdrop="static"
      data-bs-keyboard="false"
      tabIndex="-1"
      aria-labelledby={`modal-title-${props.id}`}
      aria-hidden="true"
    >
      <div className="modal-dialog">
        <div className="modal-content">
          <div className="modal-header">
            <h5 className="modal-title" id={`modal-title-${props.id}`}>
              Modal title
            </h5>
            <button
              type="button"
              className="btn-close"
              data-bs-dismiss="modal"
              aria-label="Close"
            ></button>
          </div>
          <div className="modal-body">编辑内容...</div>
          <div className="modal-footer">
            <button
              type="button"
              className="btn btn-secondary"
              data-bs-dismiss="modal"
            >
              取消
            </button>
            <button type="button" className="btn btn-primary">
              保存
            </button>
          </div>
        </div>
      </div>
    </div>
  );
});

export default Model;
修改App.js(支持多Modal管理)
function App() {
  const [modalList, setModalList] = useState([]);
  const modalRefs = useRef([]); // 用数组存储多个Modal的ref

  useEffect(() => {
    // 初始化第一个Card和Modal
    setModalList(prev => [...prev, { id: 0 }]);
  }, []);

  // 添加新的Card和Modal
  function Add() {
    const newId = modalList.length;
    setModalList(prev => [...prev, { id: newId }]);
  }

  // 唤起指定ID的Modal(Card组件可通过Context调用此方法)
  const openModal = (id) => {
    modalRefs.current[id]?.show();
  };

  return (
    <>
      <CardContext.Provider value={{ Add, openModal }}>
        {modalList.map((item) => (
          <div key={item.id}>
            {/* Card组件点击编辑按钮时调用openModal(item.id) */}
            <Card onEditClick={() => openModal(item.id)} />
            <Model 
              ref={el => modalRefs.current[item.id] = el} 
              id={item.id} 
            />
          </div>
        ))}
      </CardContext.Provider>
    </>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 17:32:08