javascript - React with D3 - 只渲染新项目而不更新或删除旧项目
问题描述
我创建了一个按预期工作的尺寸图例,除了当我对其进行更改(例如更新数据)时 - 旧版本保持呈现,新版本呈现在顶部。如何按照 D3 的预期实现更新、添加和删除元素?
我知道 React 和 D3 都想控制 DOM,所以很难一起工作,而且我的代码到目前为止来自非反应教程 - 所以我相信错误一定与我的方式有关一起使用 React 和 D3。
这是我的代码
import React, { useRef, useEffect } from 'react';
import { select, scaleSqrt } from 'd3';
function D3SizeBar(props) {
const svgRef = useRef();
useEffect(() => {
const sizeLegend = (selection, props) => {
const {
sizeScale,
spacing,
textOffset,
numTicks,
circleFill
} = props;
const ticks = sizeScale.ticks(numTicks).filter(d => d !== 0)
const groups = selection.selectAll('g').data(ticks);
const groupsEnter = groups.enter().append('g');
groupsEnter
.merge(groups)
.attr(`transform`, (d, i) =>
`translate(0, ${i * spacing})`
);
groups
.exit()
.remove();
groupsEnter
.append('circle')
.merge(groups.select('circle'))
.attr('r', sizeScale)
.attr('fill', circleFill)
groupsEnter
.append('text')
.merge(groups.select('text'))
.text(d => d)
.attr('dy', '0.32em')
.attr('x', d => sizeScale(d) + textOffset)
}
const data = [0, 0]
data.splice(0, 1, Math.max.apply(Math, props.sValueArray))
data.splice(1, 1, Math.min.apply(Math, props.sValueArray))
console.log(data)
const sizeScale = scaleSqrt()
.domain(data)
.range([20, 5])
const svg = select(svgRef.current);
svg
.append('g')
.attr('transform', `translate(30,20)`)
.call(sizeLegend, {
sizeScale,
spacing: 25,
textOffset: 10,
numTicks: 3,
circleFill: 'rgba(0, 0, 0, 0.5)'
}
);
}, [props.sValueArray]);
return (
<svg ref={svgRef}/>
)
}
export default D3SizeBar;
如果有人足够精通同时使用 React 和 D3,我将非常感谢您对我哪里出错的帮助。`sValueArray' 只是一个具有最大值的数字数组。值 = 50 和最小值。值 = 10。
这里有一个例子,将间距从 25 更改为 30。
解决方案
这是因为您在添加其他元素之前没有清除元素的内容。
您正在ref
从元素中获取<svg />
元素,并通过选择元素d3.select (ref.current)
,但 ref
每次useEffect
运行时都保持不变,并且在您运行时select (ref.current)
它已经有了孩子。因为每次React
发送一个新的网络更新, useEffect
都被更新并ref
保持不变。
解决方案
svg
在添加新对象之前,您可以简单地清理父对象中存在的所有子对象:
svg.selectAll("*").remove();
代码
import React, { useRef, useEffect } from 'react';
import { select, scaleSqrt } from 'd3';
function D3SizeBar(props) {
const svgRef = useRef();
useEffect(() => {
const sizeLegend = (selection, { sizeScale,
spacing,
textOffset,
numTicks,
circleFill }) => {
const ticks = sizeScale.ticks(numTicks).filter(d => d !== 0)
const groups = selection.selectAll('g').data(ticks);
const groupsEnter = groups.enter().append('g');
groupsEnter
.merge(groups)
.attr(`transform`, (d, i) =>
`translate(0, ${i * spacing})`
);
groups
.exit()
.remove();
groupsEnter
.append('circle')
.merge(groups.select('circle'))
.attr('r', sizeScale)
.attr('fill', circleFill)
groupsEnter
.append('text')
.merge(groups.select('text'))
.text(d => d)
.attr('dy', '0.32em')
.attr('x', d => sizeScale(d) + textOffset)
}
const data = [0, 0]
data.splice(0, 1, Math.max.apply(Math, props.sValueArray))
data.splice(1, 1, Math.min.apply(Math, props.sValueArray))
console.log(data)
const sizeScale = scaleSqrt()
.domain(data)
.range([20, 5])
const svg = select(svgRef.current);
svg.selectAll("*").remove() // add this before the append.
svg
.append('g')
.attr('transform', `translate(30,20)`)
.call(sizeLegend, {
sizeScale,
spacing: 50,
textOffset: 10,
numTicks: 3,
circleFill: 'rgba(0, 0, 0, 0.5)'
}
);
}, [props.sValueArray]);
return (
<svg ref={svgRef} />
)
}
export default D3SizeBar;
推荐阅读
- java - java中带有变量类型的圆括号是什么意思?
- javascript - 使用 for 循环向页面动态添加字符串的问题
- git - GIT合并不相关的历史
- vue.js - 为什么通过 scrollBy() 滚动在 Vue 组件更新挂钩中不起作用?
- python - 如何通过标签获取所有元素?
- angular - 显式指定材质下拉选择的类型而不是动态
- geospatial - 可以使用空间索引加速“单个几何内的点”查询吗?
- android - 刷新小部件上的现有数据
- excel - 跨工作表的 Excel VBA 克隆(命名)表
- clojure - clj-http 异常:ExceptionInfo clj-http: 状态 415 clj-http.client/wrap-exceptions/fn--1863 (client.clj:196)