partition = data => {
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
return d3.partition()
.size([2 * Math.PI, root.height + 1])
(root);
}
Some downsides to this:* Zooming is non-trivial, you have to recreate it in React proper
* React has to re-render entire hierarchies or massive lists depending on the traversal method that you choose to use for hierarchical datasets (topological or depth first) and React chokes on these pretty easily since they tend to be wide and flat
* Quickly changing data sets can't really benefit from memoization or React.memo, since the layouts themselves will constantly change. It makes things a bit awkward when mixing with memoizable data.
However, once we got the groundwork down, we never really had to touch it again. It was much easier to maintain than the pure d3 implementation using useEffect or componentDidMount, since we didn't have to deal with any sort of DOM state ourselves any more.
I essentially have a visualization class with an svg, and an update function for all the D3 stuff. The data is handed using props.
When the component mounts, or updates I call my own update function which sets the svg size, binds the data, and defines the enter/update/exit actions+transitions. Because I call update in the componentDidUpdate function, I had to catch 'fake' updates where the data hadn't changed, and abstain from state-updates such that I don't end up with infinite update loops.
componentDidMount() {
this.update();
}
componentDidUpdate(prevProps, prevState) {
this.update();
}
render() {
return (
<div ref={this.gridRef} className={"VisContainer"}>
<svg className={"VisSVG"} ref={this.svgRef} />
</div>
);
}
With this rather primitive structure, my biggest problem was with d3. While many tutorial cover the enter/update/exit model, they often don't go in-depth wth the key-function of the data binding. Probably because they often use date of form list<int> for their tutorial.
In my case I had more complex data such that I needed to have efficient updates, using the filter function of d3.This is largely why I wanted to write a post like this - often "using d3.js" is what we think we need to do, but I wanted to show that most of the API is just utility functions. Here are the two modules devoted to manipulating the DOM:
https://wattenberger.com/blog/d3#manipulating-the-dom
I recommend (and follow) that you render elements like you normally would, and use things like d3 scales or d3.line() to convert your data into the physical aspects that are needed. I find it really hacky to do it any other way. I want to explore more, but I did a quick test to see which is more performant (letting React render a scatter plot, or letting d3 render a scatter plot), and pure React was 4x faster:
https://twitter.com/Wattenberger/status/1123413424678027265
Of course, this was just one example, but it agrees with my hypothesis.
I do have a whole chapter devoted to d3 + React (and one on d3 + Angular) that might be worth checking out. I think it largely comes down to understanding your tools and being able to pick the parts of d3 that will be most useful to you.
https://github.com/kay-is/react-from-zero/blob/master/16-adv...