React with D3 - 只渲染新项目而不更新或删除旧项目
React with D3 - Only rendering new items without updating or removing old ones
我创建了一个尺寸图例,除了当我对其进行更改时(例如更新数据——旧版本保持渲染状态,新版本在顶部渲染。如何按照 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。
这是因为您在添加其他元素之前没有清除该元素的内容。
您从 <svg />
元素中取出 ref
,并通过 d3.select (ref.current)
选择元素,但 ref
每次都保持不变 useEffect
是 运行 ,就在你 运行 select (ref.current)
时它已经有了 children。因为每次 React
发送一个新的网络更新, useEffect
被更新而 ref
保持不变。
解决方案
在添加新的 object:
之前,您可以简单地清除 parent svg
中存在的所有 children
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;
我创建了一个尺寸图例,除了当我对其进行更改时(例如更新数据——旧版本保持渲染状态,新版本在顶部渲染。如何按照 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。
这是因为您在添加其他元素之前没有清除该元素的内容。
您从 <svg />
元素中取出 ref
,并通过 d3.select (ref.current)
选择元素,但 ref
每次都保持不变 useEffect
是 运行 ,就在你 运行 select (ref.current)
时它已经有了 children。因为每次 React
发送一个新的网络更新, useEffect
被更新而 ref
保持不变。
解决方案
在添加新的 object:
之前,您可以简单地清除 parentsvg
中存在的所有 children
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;