React is great. d3 is great. Both excel at their core missions but integrating the two proposes some challenges. React wants to control all state and the DOM, whereas d3 wants to bind data to elements and control related state transitions.
There is a lot of conflicting advice about how to include d3 components in a React application, though the advice tends toward a React bias, whereby solutions are forced into the “React way” and React controls everything. If you’re willing to part from the gospel, however, it’s actually pretty easy to get the best of both worlds.
React still manages the application state, passing props into our Chart instance, but does not manage the DOM state for the chart. This enables us to use d3 patterns and transitions as usual without worrying about keeping the React virtual DOM up to speed.
The data for the chart are passed in as props, as is the chart size, which we update on window resize events. The end result will be the following contrived chart. Click the “refresh” button to generate new random data and confirm that the transitions are working; try resizing your browser window to see the chart size update. The full code is available here though the relevant files are described inline below.
The main entry point for the application is the App class, which renders a button (to generate new data) and the ChartContainer:
This is a pretty standard React component. We store the data and chart size in state and pass those along to the ChartContainer component during rendering. Clicking the button generates a new data set, while a window resize event triggers a new value for the chart size. The ChartContainer class is as follows:
The ChartContainer class is fairly straightforward. When the component mounts, add the chart. When props update, update the chart. When the component unmounts, delete the chart. And we just render an empty div container that’s the parent for the chart.
The Chart class referenced above is as follows:
The Chart class contains all of our d3 code. The render() method is called once on instantiation and creates the static elements such as the main svg node and needed scales and axes. The update() method includes the standard d3 enter-update-exit pattern. This approach was used to construct the distribution viewer and trigonometry visualization tools on this site.