Got this piece of code:
export default function Chart({ data, changeData}) {
console.log(data);
const ref = useRef();
const createGraph = (data) => {
var margin = { top: 20, right: 20, bottom: 30, left: 40 },
width = 1360 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand().range([0, width]).padding(0.1);
var y = d3.scaleLinear().range([height, 0]);
var svg = d3
.select(ref.current)
.append("svg")
.attr("id", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(
data.map(function (d) {
return d.name;
})
);
y.domain([
0,
d3.max(data, function (d) {
return d.number;
}),
]);
// append the rectangles for the bar chart
const bars = svg.selectAll().data(data).enter().append("rect");
bars
.attr("class", "bar")
.attr("x", function (d) {
return x(d.name);
})
.attr("width", x.bandwidth())
.attr("y", function (d) {
return y(d.number);
})
.attr("fill", "pink")
.attr("height", function (d) {
return height - y(d.number);
})
.on("mouseenter", function (actual, i) {
d3.select(this).attr("opacity", 0.5);
d3.select(this)
.transition()
.duration(300)
.attr("opacity", 0.6)
.attr("x", (a) => x(a.name) - 5)
.attr("width", x.bandwidth() + 10);
})
.on("mouseleave", function (actual, i) {
d3.select(this).attr("opacity", 1);
d3.select(this)
.transition()
.duration(300)
.attr("opacity", 1)
.attr("x", (a) => x(a.name))
.attr("width", x.bandwidth());
});
bars
.append("text")
.attr("class", "value")
.attr("x", (a) => x(a.name) + x.bandwidth() / 2)
.attr("y", (a) => y(a.number) + 30)
.attr("fill", "blue")
.attr("text-anchor", "middle")
.text((a) => `${a.number}%`);
d3.selectAll("bars").append("text").attr("class", "divergence");
// add the x Axis
svg
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g").call(d3.axisLeft(y));
};
useEffect(() => {
createGraph(data);
}, [data]);
return (
<React.Fragment>
<Filter>
<h2>By</h2>
<span>Popularity</span>
<span onClick={() => changeData()}>Following</span>
</Filter>
<div style={{ marginLeft: "100px" }} ref={ref}></div>
</React.Fragment>
);
}
How does one update the data with every render?
I tried creating a function that removes the SVG and adds a new one but the positions are messed up after first render, also I tried using exit() and remove() in useEffect but no results.
The result that I'm trying to achieve is that I have a graph and the data is fetched and passed into this component and whenever I press a button, the data will change. I'm not looking at any animations right now, I just want to see how I can change the data.
It's a bit late but in case anyone happens on this question while searching.
useEffect(() => {
*// your d3 code here*
instead of using .enter().append('rect')
use .join('rect')
}, [data]) *// runs the d3 code every time the data changes*
more about.join() https://observablehq.com/@d3/selection-join
In the return statement specify the svg and g tags.
Instead of ref on the div, do useRef on the svg tag
Instead of.append(g), do.select('.x-axis') etc
This is so that you do not append new g every time the useEffect is run
<svg ref={d3Container}>
<g className="chart">
<g className="x-axis" />
<g className="y-axis" />
</g>
</svg>
I learnt the above after watching the 1st and 3rd video in this series https://muratorium.com/using-react-hooks-with-d3
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.