React+D3图表无法渲染Context映射数据,静态数据正常
问题描述
我是React和D3的新手,尝试在带有背景图的网格(x/y轴)上展示每条记录的小图标数据。使用静态数据gcs时,D3图表可正常工作;但从CohortContext中过滤映射得到的goalCohorts数据无法在图表上显示。我怀疑是D3渲染早于数据返回的时序问题,尝试用async包裹useEffect但未解决。控制台显示goalCohorts数据与静态数据结构一致,使用React v18.1、D3 v7.8,相关代码如下:
import React, { useEffect, useContext } from "react" import * as d3 from "d3" import logo from '../../assets/images//charts/360Chart.svg' import CohortContext from "../../utility/context/cohort/cohort-context" import Saboteur from '../../assets/images/charts/chartBubbleKey.svg' function ChartData({ goal }) { const gcs = [ { chartAsset: Saboteur, xCoordinates: 200, yCoordinates: 300 }, { chartAsset: Saboteur, xCoordinates: 150, yCoordinates: 150 } ] console.log(gcs) const width = 800 const height = 800 const { cohorts } = useContext(CohortContext) useEffect(() => { //Retrieve the Goal data from the useContext const fetchData = async () => { const goalCohorts = cohorts .filter(records => records.goalId === goal.goalId) .map((records) => { return { chartAsset: Saboteur, xCoordinates: records.xcoordinates, yCoordinates: records.ycoordinates } }) console.log('goal cohorts for Chart :', goalCohorts) const svg = d3 .select("#svgcontainer") .append("svg") .attr("width", width) .attr("height", height) //background Chart svg .append("svg:image") .attr("xlink:href", () => logo) .attr("width", 580) .attr("height", 580) .attr("x", 110) .attr("y", 20) // Add in the data for the chart data const g = svg .selectAll("g") .append("g") .data(goalCohorts) // works if I use .data(gcs) .enter() g.append("svg:image") .attr("xlink:href", (data) => data.chartAsset) .attr("width", 50) .attr("height", 50) .attr("x", (data) => data.xCoordinates) .attr("y", (data) => data.yCoordinates) } fetchData() }, []) return ( < div className="App" > <div id="svgcontainer"></div> < /div > ) } export default ChartData
问题原因及修复方案
依赖项缺失导致useEffect未触发更新:你的
useEffect依赖数组为空,初始渲染时cohorts可能还未从Context中加载完成,导致goalCohorts为空;后续cohorts或goal更新时,useEffect也不会重新执行生成新图表。
修复:将cohorts和goal.goalId加入依赖数组:useEffect(() => { // ... 原有逻辑 }, [cohorts, goal.goalId])D3数据绑定顺序错误:你先执行
.append("g")再绑定数据,这会把数据绑定到一个空的<g>元素上,而非基于数据创建新元素。正确流程是先选择元素、绑定数据、进入阶段,再创建元素。另外,给选择器加类名避免选中背景图相关元素:
修复:调整数据绑定代码:// 正确的数据绑定流程 const g = svg .selectAll("g.chart-item") // 用类名精准选择 .data(goalCohorts) .enter() .append("g") // 进入阶段后创建对应数量的g元素 g.append("svg:image") .attr("href", (data) => data.chartAsset) // D3 v7推荐使用href替代xlink:href .attr("width", 50) .attr("height", 50) .attr("x", (data) => data.xCoordinates) .attr("y", (data) => data.yCoordinates)重复创建SVG元素:每次
useEffect执行都会往容器追加新SVG,旧图表残留且新图表可能被覆盖。需要在创建前清空容器:
修复:创建SVG前清空容器内容:const svgContainer = d3.select("#svgcontainer"); svgContainer.html(""); // 清空原有内容 const svg = svgContainer .append("svg") .attr("width", width) .attr("height", height);冗余的async函数:
fetchData内没有异步操作,无需用async包裹,去掉后可避免不必要的异步时序问题。
内容的提问来源于stack exchange,提问作者Clarkie
相关产品推荐
相关产品推荐

