Ant-Design modal对话框未打开时,无法通过uesRef获取modal内部元素DOM节点

发布时间 2023-11-29 17:48:37作者: 飞奔的程序员

为什么要记录下来呢?因为我在网上和chatGpt上没有搜到合适的解决方案。在CDNS上看到个和我遇到问题一样的,居然要收费才能看,所以自己记下来。当然肯定还有其他的好方案,欢迎大家留言。

需求:使用antdV/g6画关系图,类似于企查查上面的那样:点击按钮 打开Modal框,把数据渲染到 Modal框的div上。

 遇到的问题:打开Modal时,图渲染不上去,打印ref.current是null,关闭Modal框后,打印的ref.current是 div元素

**以下是父组件代码: 结构图按钮实际是在表格行内的,这里只是模拟**

import React, { useEffect, useState } from 'react';
import { message } from 'antd';
import { StructureGraph } from '@/components/GlobalComponents';
const [imgVisible, setImgVisible] = useState(false); // 结构图弹窗
const [curClickRowData, setCurClickRowData] = useState({}); // 点击的当前行数据
const [imgData, imgLoading, onRequestImg] = useServiceApi(qryProductStructureGraph);

export const ProductInvestmentStatement = () => {
        // 点击结构图按钮
      const handleClickStructure = (record:any) => {
        setCurClickRowData(record);
        onRequestImg({
          productId: record.productId,
        }).then((res) => {
          if (res?.tradeNode?.tradeNodeVo) {
            setImgVisible(true);
          } else {
            message.error('此项目还没有结构图!');
          }
        });
      };
      
      return <>
          <div onClick={handleClickStructure}>结构图</div>
          <StructureGraph
            structureData={imgData?.tradeNode || {}}
            loading={imgLoading}
            id={curClickRowData.productId}
            name={curClickRowData.displayShortName}
            visible={imgVisible}
            onCancel={() => setImgVisible(false)}
          />
      </>
  }

 

接下来是子组件代码,已去掉图相关的代码  

import React, { useEffect, useRef, useMemo, useState } from 'react';
import { ConfirmModal } from '@/components/GlobalComponents'; // 其实就是antd的Modal框封装了一下样式

export const StructureGraph = ({ structureData, id, visible, onCancel, name }) => {
// 这里使用自定义hook对接口数据做相关的处理
  const data = useMemo(() => traverse(structureData, id), [structureData]);
  const ref = useRef<HTMLDivElement>(null);
    useEffect(() => {
        if (!data || !Object.keys(data).length) return;
        if (ref?.current) {
          console.log(ref?.current, '3333333333');
        }
    }, [data, visible]);
    
    return (<ConfirmModal
    modalWidth="1140px"
    visible={visible}
    title={`${name} 结构图`}
    cancelText="关闭"
    onCancel={onCancel}
    noOkBtn
  >
    <div style={{ width: '100%', height: 500, userSelect: 'none' }} ref={ref} />
  </ConfirmModal>);
}

  

 

由以上可以看到,Modal框没有渲染在DOM上,但是你的组件其实已经渲染了,这种情况是拿不到 div的ref的。

第一种解决方案: visible为false时,整个组件return null; 但是这样Modal框关闭的动效就没有了,UI应该不能接受。

import React, { useEffect, useRef, useMemo, useState } from 'react';
import { ConfirmModal } from '@/components/GlobalComponents'; // 其实就是antd的Modal框封装了一下样式

export const StructureGraph = ({ structureData, id, visible, onCancel, name }) => {
// 这里使用自定义hook对接口数据做相关的处理
  const data = useMemo(() => traverse(structureData, id), [structureData]);
  const ref = useRef<HTMLDivElement>(null);
    useEffect(() => {
        if (!data || !Object.keys(data).length) return;
        if (ref?.current) {
          console.log(ref?.current, '3333333333');
        }
    }, [data, visible]);
    
   // 加上这个就可以了。 但是这样Modal框关闭的动效就没有了
   if (!visible) {
      return null; // 不渲染 Modal
   }
    
    return (<ConfirmModal
    modalWidth="1140px"
    visible={visible}
    title={`${name} 结构图`}
    cancelText="关闭"
    onCancel={onCancel}
    noOkBtn
  >
    <div style={{ width: '100%', height: 500, userSelect: 'none' }} ref={ref} />
  </ConfirmModal>);
}

 第二种解决方案: 使用useState新建个newRef,监听这个元素 Modal框动效正常 

 

export const StructureGraph = ({ structureData, id, visible, onCancel, name }) => {
// 这里使用自定义hook对接口数据做相关的处理
  const data = useMemo(() => traverse(structureData, id), [structureData]);
  const ref = useRef<HTMLDivElement>(null);
  const [newRef, setNewRef] = useState(ref); // 通过多创建一个ref,来解决 Modal框未渲染时,拿不到div的问题
  
    useEffect(() => {
        if (!data || !Object.keys(data).length) return;
        if (ref?.current) {
          console.log(ref?.current, '3333333333');
        }
    }, [data, newRef]); //这里监听newRef
    
    useEffect(() => {
    if (visible) {
      setNewRef(ref);
    } else {
      setNewRef(null);
    }
  }, [visible]);
    
    return (<ConfirmModal
    modalWidth="1140px"
    visible={visible}
    title={`${name} 结构图`}
    cancelText="关闭"
    onCancel={onCancel}
    noOkBtn
  >
    <div style={{ width: '100%', height: 500, userSelect: 'none' }} ref={ref} />
  </ConfirmModal>);
}

 

 最后上图: