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

React Hooks中useEffect获取div为null引发Highcharts报错问题

问题根源

你碰到这个问题和useEffect是否等价于componentDidMount没有直接关系,核心原因有两个:

  • 异步函数存在执行时序断层
    useEffect的回调本身确实是在首次DOM挂载完成后同步触发的,但你在useEffect里调用的fetchChartData是async异步函数:当函数执行到await fetchChartData(接口请求)这一行时,会暂停当前函数执行,把后续逻辑(console打印、Highcharts初始化)推入微任务队列,等待接口请求完成后再执行。
    这段等待接口返回的时间里,DOM完全可能发生变化:最常见的就是React 18开发环境严格模式会自动执行「挂载→卸载→重新挂载」的双调用校验流程,另外父组件条件渲染临时移除当前Chart组件、路由切换导致组件卸载也会触发DOM变动。等接口终于返回时,首次渲染生成的id="data-consumed"节点早就被从DOM树中移除了,此时调用document.getElementById("data-consumed")自然返回null。你描述的「首次打印拿null、后续执行能拿到DOM」的现象,刚好和严格模式的双执行逻辑完全吻合。
  • DOM获取方式不符合React设计逻辑
    直接通过id全局查找DOM本身就不可靠:如果页面上存在多个同id的元素、组件重渲染后DOM节点被替换,全局查找很容易拿到错误引用或者null,Highcharts抛出的Error #13就是初始化时找不到指定渲染容器的典型报错。
修复方法

按下面的逻辑调整代码即可解决问题:

  • 用useRef绑定渲染容器,代替全局id查找,保证拿到的始终是当前组件实例对应的DOM节点
  • 增加组件卸载状态标记,避免组件卸载后、接口返回时再执行Highcharts初始化的无效操作
  • 把依赖项正确加入useEffect依赖数组,避免闭包陷阱

修复后的核心代码示例:

import { useEffect, useRef } from 'react';
import Highcharts from 'highcharts';
import moment from 'moment';

const Chart2 = (props) => {
  // 用ref绑定图表容器
  const chartContainerRef = useRef(null);
  const { fetchChartData } = props;

  useEffect(() => {
    // 标记组件是否已卸载,处理异步竞态
    let isUnmounted = false;

    const initChart = async () => {
      const startDate = moment().startOf("year").toISOString();
      const endDate = moment().endOf("year").toISOString();
      const data = await fetchChartData({ startDate, endDate, userId: "XXXXXXX" });
      
      // 组件已经卸载、或容器不存在就不再执行后续逻辑
      if (isUnmounted || !chartContainerRef.current) return;

      Highcharts.chart(chartContainerRef.current, {
        chart: {
          type: "column",
        },
        title: {
          text: null,
        },
        subtitle: {
          text: "Source: WorldClimate.com",
        },
        xAxis: {
          categories: data.map(({ _id }) => _id),
          crosshair: true,
        },
        yAxis: {
          min: 0,
          title: {
            text: "Rainfall (mm)",
          },
        },
        plotOptions: {
          column: {
            pointPadding: -0.1,
            borderWidth: 0,
            color: "#5036D6",
          },
        },
        series: [
          {
            name: null,
            data: data.map(({ processingStorage }) => processingStorage),
          },
        ],
      });
    };

    initChart();

    // 组件卸载时更新标记
    return () => {
      isUnmounted = true;
    }
  }, [fetchChartData]);

  return (
    <div className="chart-container cursor-pointer">
      <div className="chart-block">
        <h4>Data Consumed</h4>
        {/* 把ref绑定到容器上,移除容易冲突的硬编码id */}
        <div ref={chartContainerRef} className="graph-block" />
      </div>
    </div>
  );
};

export default Chart2;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:48:29