我正在开发一个 React 组件,该组件使用了 D3,但是 svg 圆圈仅触发一次。
import React, { useEffect, useRef, useState } from 'react';
import * as d3 from 'd3';
import D3ColorLegend from './D3ColorLegend';
import {Tooltip} from './ToolTip'
const Scatterplot = ({ data, colorProperty, hoverProp }) => {
const svgRef = useRef();
const [details, setDetails] = useState(null);
useEffect(() => {
const handleMouseOver = (event, d) => {
const [x, y] = d3.pointer(event);
setDetails({
xPos : x,
yPos : y,
name: hoverProp[data.indexOf(d)],
});
};
// set the dimensions and margins of the graph
const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 800 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;
// Find the minimum and maximum values in your data for both x and y
const xExtent = d3.extent(data, d => d.x);
const yExtent = d3.extent(data, d => d.y);
// Append the SVG object to the body of the page
const svg = d3.select(svgRef.current)
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// Add X axis
const x = d3.scaleLinear()
.domain([Math.min(0, xExtent[0]), xExtent[1]])
.range([0, width]);
const xAxis = svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x));
// Add Y axis
const y = d3.scaleLinear()
.domain([Math.min(0, yExtent[0]), yExtent[1]])
.range([height, 0]);
const yAxis = svg.append('g')
.call(d3.axisLeft(y));
// Add a clipPath: everything out of this area won't be drawn.
const clip = svg.append('defs').append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', width)
.attr('height', height)
.attr('x', 0)
.attr('y', 0);
// Create the scatter variable: where both the circles and the brush take place
const scatter = svg.append('g')
.attr('clip-path', 'url(#clip)');
const colorScale = d3.scaleSequential().domain([1, 10]).interpolator(d3.interpolateSinebow);
// Add circles
scatter
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', (d) => x(d.x))
.attr('cy', (d) => y(d.y))
.attr('r', 8)
.style('fill', (_, i) => colorScale(colorProperty[i]))
.style('opacity', 0.5)
.on("mouseover", () => {} )
.on('mousemove', (event, d) => handleMouseOver(event, d))
.on('mouseleave', () => setDetails(null));
// Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
const zoom = d3.zoom()
.scaleExtent([0.5, 20]) // This controls how much you can unzoom (x0.5) and zoom (x20)
.extent([[0, 0], [width, height]])
.on('zoom', updateChart);
// This adds an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
svg.append('rect')
.attr('width', width)
.attr('height', height)
.style('fill', 'none')
.style('pointer-events', 'all')
.attr('transform', `translate(${margin.left},${margin.top})`)
.call(zoom);
// now the user can zoom, and it will trigger the function called updateChart
// A function that updates the chart when the user zooms and thus new boundaries are available
function updateChart(event) {
// recover the new scale
const newX = event.transform.rescaleX(x);
const newY = event.transform.rescaleY(y);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX));
yAxis.call(d3.axisLeft(newY));
// update circle position
scatter
.selectAll('circle')
.attr('cx', (d) => newX(d.x))
.attr('cy', (d) => newY(d.y));
}
}, [data]);
return (
<div className='container'>
<D3ColorLegend />
<div id="dataviz_axisZoom" ref={svgRef}></div>
{details && <Tooltip interactionData={details} />}
</div>
);
};
export default Scatterplot;
工具提示确实渲染一次,仅此而已,所以我知道工具提示可以工作。这里有一个相关的问题,但我无法访问代码,所以如果我遇到同样的问题,我就不会访问。
有什么办法可以解决这个问题吗?
您的大部分代码都在
useEffect()
中,它在其依赖项数组中使用 data
:
useEffect(() => {
..
}, [data]);
这意味着每次
useEffect()
发生变化时,data
都会触发。如果不改变,下次就不会触发。
考虑将
handleMouseOver
函数移到 useEffect()
之外。