在React的 useEffect中进行动态图表初始化时,无法读取null的属性(读取 'getContext')

前端开发 2026-07-09

我正在尝试在一个React组件中,使用useRef来定位canvas元素并初始化一个Chart.js实例。该组件在挂载时会从API异步获取数据。

如果我用硬编码的静态数据来初始化图表,效果就完全正常。但是,当我在异步API请求完成后再进行初始化时,会抛出致命错误并使应用崩溃。

import { useEffect, useRef, useState } from 'react';
import Chart from 'chart.js/auto';

export default function AnalyticsDashboard() {
  const canvasRef = useRef(null);
  const [analyticsData, setAnalyticsData] = useState(null);
  const [loading, setLoading] = useState(true);

  // 1. Fetch data on mount
  useEffect(() => {
    async function fetchMetrics() {
      try {
        const response = await fetch('/api/v1/analytics');
        const data = await response.json();
        setAnalyticsData(data); // e.g., { labels: ['A', 'B'], values: [10, 20] }
      } catch (err) {
        console.error("Failed to fetch", err);
      } finally {
        setLoading(false);
      }
    }
    fetchMetrics();
  }, []);

  // 2. Initialize chart when data arrives
  useEffect(() => {
    if (!analyticsData) return;

    // ERROR THROWN HERE 👇
    const ctx = canvasRef.current.getContext('2d'); 

    const chartInstance = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: analyticsData.labels,
        datasets: [{ data: analyticsData.values }]
      }
    });

    return () => chartInstance.destroy();
  }, [analyticsData]);

  if (loading) return <div>Loading metrics...</div>;

  return (
    <div className="chart-container">
      <h2>Monthly Traffic</h2>
      <canvas ref={canvasRef} id="trafficChart" width="400" height="200"></canvas>
    </div>
  );
}

错误控制台输出:

Uncaught TypeError: Cannot read properties of null (reading 'getContext')
    at AnalyticsDashboard.js:31:35
    at commitHookEffectListMount (react-dom.development.js:23150:26)
    at commitPassiveMountOnFiber (react-dom.development.js:24408:13)

我尝试过的做法:

  1. 我在调用getContext之前加上了if (!canvasRef.current) return;。错误消失了,但图表根本不渲染——canvasRef.current一直保持为null。

  2. 如果我移除if (loading) return...的提前返回状态,能工作,但页面看起来会很破碎,因为在API加载完成之前,它尝试用空数据渲染标题。

为什么useEffect里面的ref会是null,即使canvas元素在JSX中是明确存在的?我该如何修复这里的时序问题?

解决方案

我没能用一个简单的示例重现,但我猜这与状态更新的时序有关。

目前,你可能会处于一种你没有考虑到的状态:analyticsData 不是 null(或 undeifned),而 loading 仍然是 true

在这种情况下,由于提前返回你不会渲染画布,但副作用已经尝试访问该ref。

我会用更强的条件来保护第二个副作用:

if (!analyticsData || loading) return;

为了更好地调试时序,你可以将设置ref的函数传入 ref 属性:

<canvas
  ref={(node) => {
    canvasRef.current = node;
    console.log("canvas node:", node);
  }}
/>
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章