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

React中使用D3绘制折线图出现NaN报错求助

报错原因与修复方案

1. 报错根源

你犯了React钩子的基础使用错误:useState返回的是包含状态值和更新函数的数组,而非直接的状态值。你写的const data = useState([35, 78, 38]);会让data变成[[35,78,38], setData]的结构,当D3尝试用这个数组生成路径时,yScale处理到第二个元素(更新函数)时无法解析为数字,直接返回NaN,就出现了<path> attribute d: Expected number的报错。

2. 修复后的代码

修正useState的解构方式,同时补全样式单位(避免样式不生效),并优化了y轴缩放逻辑:

import React, { useState, useRef, useEffect } from "react";
import * as d3 from 'd3'

export const Chart = React.forwardRef((props, ref) => {
    // 正确解构useState,获取纯数据数组
    const [data] = useState([35, 78, 38]);
    const svgRef = useRef();


    useEffect(() => {
        const w = 400;
        const h = 100;
        const svg = d3.select(svgRef.current)
            .attr('width', w)
            .attr('height', h)
            .style('background', '#d3d3d4')
            .style('margin-left', '10px') // 补全单位px
            .style('margin-top', '10px')  // 补全单位px
            .style('overflow', 'visible')

        // 坐标轴缩放设置
        const xScale = d3.scaleLinear()
            .domain([0, data.length - 1])
            .range([0, w]);

        // 用数据最大值作为y轴范围,适配数据高度
        const yScale = d3.scaleLinear()
            .domain([0, d3.max(data)])
            .range([h, 0]);
            
        const generateScaledLine = d3.line()
            .x((d, i) => xScale(i))
            .y(yScale) 
            .curve(d3.curveCardinal)

        // 绘制折线
        svg.selectAll('.line')
            .data([data])
            .join('path')
            .attr('d', d => generateScaledLine(d))
            .attr('fill', 'none')
            .attr('stroke', 'black')
            .attr('stroke-width', 2); // 增加线条宽度,提升可读性

    }, [data]);
    return ( 
    <div>
        <svg ref={ svgRef } />
    </div>
    );
});

3. React 18 + D3 新手友好示例

示例1:基础柱状图

import React, { useRef, useEffect } from 'react';
import * as d3 from 'd3';

export const BarChart = () => {
  const svgRef = useRef();
  const data = [25, 40, 65, 80, 30];

  useEffect(() => {
    const width = 400;
    const height = 200;
    const margin = { top: 20, right: 20, bottom: 30, left: 40 };

    const svg = d3.select(svgRef.current)
      .attr('width', width)
      .attr('height', height);

    // 清除旧内容,避免重复渲染
    svg.selectAll('*').remove();

    // 坐标轴缩放
    const xScale = d3.scaleBand()
      .domain(data.map((_, i) => `Item ${i+1}`))
      .range([margin.left, width - margin.right])
      .padding(0.2);

    const yScale = d3.scaleLinear()
      .domain([0, d3.max(data)])
      .range([height - margin.bottom, margin.top]);

    // 绘制柱子
    svg.selectAll('rect')
      .data(data)
      .join('rect')
      .attr('x', (d, i) => xScale(`Item ${i+1}`))
      .attr('y', d => yScale(d))
      .attr('width', xScale.bandwidth())
      .attr('height', d => height - margin.bottom - yScale(d))
      .attr('fill', '#4a90e2');

    // 绘制坐标轴
    const xAxis = d3.axisBottom(xScale);
    const yAxis = d3.axisLeft(yScale);

    svg.append('g')
      .attr('transform', `translate(0, ${height - margin.bottom})`)
      .call(xAxis);

    svg.append('g')
      .attr('transform', `translate(${margin.left}, 0)`)
      .call(yAxis);

  }, [data]);

  return <svg ref={svgRef} />;
};

示例2:动态更新折线图

import React, { useState, useRef, useEffect } from 'react';
import * as d3 from 'd3';

export const DynamicLineChart = () => {
  const [data, setData] = useState([10, 30, 20, 50, 40]);
  const svgRef = useRef();

  // 模拟自动更新数据
  useEffect(() => {
    const interval = setInterval(() => {
      setData(prev => {
        const newData = [...prev];
        newData.push(Math.floor(Math.random() * 60));
        return newData.slice(-5); // 只保留最新5个数据点
      });
    }, 2000);

    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    const width = 400;
    const height = 200;
    const margin = { top: 20, right: 20, bottom: 30, left: 40 };

    const svg = d3.select(svgRef.current)
      .attr('width', width)
      .attr('height', height);

    svg.selectAll('*').remove();

    // 坐标轴缩放
    const xScale = d3.scaleLinear()
      .domain([0, data.length - 1])
      .range([margin.left, width - margin.right]);

    const yScale = d3.scaleLinear()
      .domain([0, d3.max(data)])
      .range([height - margin.bottom, margin.top]);

    // 生成折线
    const line = d3.line()
      .x((d, i) => xScale(i))
      .y(d => yScale(d))
      .curve(d3.curveMonotoneX);

    // 绘制折线
    svg.append('path')
      .datum(data)
      .attr('fill', 'none')
      .attr('stroke', '#e74c3c')
      .attr('stroke-width', 2)
      .attr('d', line);

    // 绘制数据点
    svg.selectAll('circle')
      .data(data)
      .join('circle')
      .attr('cx', (d, i) => xScale(i))
      .attr('cy', d => yScale(d))
      .attr('r', 4)
      .attr('fill', '#e74c3c');

    // 绘制坐标轴
    const xAxis = d3.axisBottom(xScale)
      .tickFormat(d => `Point ${d+1}`);
    const yAxis = d3.axisLeft(yScale);

    svg.append('g')
      .attr('transform', `translate(0, ${height - margin.bottom})`)
      .call(xAxis);

    svg.append('g')
      .attr('transform', `translate(${margin.left}, 0)`)
      .call(yAxis);

  }, [data]);

  return (
    <div>
      <svg ref={svgRef} />
      <button onClick={() => setData([...data, Math.floor(Math.random() * 60)])}>添加数据点</button>
    </div>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:31:20