I'm trying to get a react component working which uses the d3 sunburst chart. The problem I'm facing is I need a way to update the zoom level of the sunburst component, as a trigger from an external component. I'm sending the node to be zoomed to via the props to the sunburst component, and that changes each time there is an external input for a different component.
Here is the pseudocode I have so far but, each the the props changes.
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const SunburstSmooth = (props) => {
const prevProps = usePrevious(props);
useEffect(() => {
if (!isEqual(prevProps, props)) {
if (props.navigateTo) {
zoomToSunburst(props.navigateTo);
} else {
if (props.data) {
renderSunburstSmooth();
update();
}
}
}
}, [props])
// Global Variables
let root, node;
let gWidth, gHeight, radius, svg;
let color;
let x, y, arc, partition;
const svgRef = useRef();
const zoomToSunburst = (nodeToRender) => {
const gWidth = props.width;
const gHeight = props.height;
const radius = (Math.min(gWidth, gHeight) / 2) - 10
const svg = d3.select(svgRef.current)
const x = d3.scaleLinear().range([0, 2 * Math.PI])
const y = d3.scaleSqrt().range([0, radius])
const partition = d3.partition()
const arc = d3.arc()
// ....
root = d3.hierarchy(nodeToRender);
node = nodeToRender;
svg.selectAll("path")
.transition("update")
.duration(1000)
.attrTween("d", (d, i) =>
arcTweenPath(d, i, radius, x, y, arc));
}
const update = () => {
root.sum(d => d[props.value]);
let gSlices = svg.selectAll("g")
.data(partition(root).descendants())
.enter()
.append("g");
gSlices.exit().remove();
gSlices.append("path")
.style('fill', (d) => {
let hue;
const current = d;
if (current.depth === 0) {
return '#c6bebe';
}
return color((current.children ? current.x0 : current.parent.x0));
})
.attr('stroke', '#fff')
.attr('stroke-width', '1')
svg.selectAll("path")
.transition("update")
.duration(750)
.attrTween("d", (d, i) =>
arcTweenPath(d, i, radius, x, y, arc));
}
// Sets up the initial sunburst
const renderSunburstSmooth = () => {
// Structure
gWidth = props.width;
gHeight = props.height;
radius = (Math.min(gWidth, gHeight) / 2) - 10;
// Size our <svg> element, add a <g> element, and move translate 0,0 to the center of the element.
svg = d3.select(svgRef.current)
.append("g")
.attr("id", "bigG")
.attr("transform", `translate(${gWidth / 2},${gHeight / 2})`);
x = d3.scaleLinear().range([0, 2 * Math.PI]);
y = d3.scaleSqrt().range([0, radius]);
// Calculate the d path for each slice.
arc = d3.arc()
// .....
// Create our sunburst data structure
partition = d3.partition();
// Root Data
root = d3.hierarchy(props.data);
node = props.navigateTo || root;
}
return (
<div id={props.keyId}>
<svg ref={svgRef}/>
</div>
);
}
A lot of the code base code is from here: http://bl.ocks.org/metmajer/5480307
Right now each time the prop is updated, the entire component is re-rendered. How do i make it so that it only updates the existing svg container, when the props.navigateTo is changed externally.