Authored by Steven Hall
I have been working in my spare time on ideas for using the new separate D3 4.0 modules with React. Below I present some examples of using the enter, update and exit pattern to create transitions in React that do not rely on using React.findDOMNode and handing over control of DOM elements to D3. I think the examples turned out well, but I would consider these prototypes for the moment.
While requiring more understanding of D3 internals, I think this approach results in cleaner code that allows visualizations to be highly optimized by the developer. D3 interpolators can be tuned according to the specific needs of the application and React remains in control of what is actually is in the DOM at any one time. I plan to add to the repo and try some more complex visualizations and see how this method holds up. Feel free to comment below if you have suggestions or comments.
Here's a link to the repository on Github...
The repo has all the code for the following examples. If you are familiar with React, Redux, and D3 it should be pretty easy to follow. Below is more high level information on the approach taken in these examples. I plan to follow up this post with a more detailed breakdown of an example for people who are less familiar with all the technologies involved.
Examples for this post:
Bar Graph Example
This example is based on the original example done by Mike Bostock to illustrate the concept of object constancy. The example demonstrates bars entering, exiting and updating when selections are made on the age groups.
Stacked Chart Example
This example demonstrates transitions performed on paths. Series can be removed and added and D3 interpolators transition the paths to their new locations. You can also change the offset type and the chart will transition the paths accordingly.
In this example I recreate the classic example that Mike Bostock used to demonstrate the general update pattern using enter, update and exit selections.
What is D3 4.0?
Although, at the time of writing it hasn't been officially released yet, D3 4.0 is fundamentally different than the 3.x versions of D3 in that it is composed of many separate modules. All of the modules are written in ES6 and can be imported individually via NPM and used directly in your application.
If you go to the D3 organization page on Github, all the modules completed to date are listed out. You'll see separate modules for things like d3-scale and d3-interpolator. You can check it out here.
There are a few significant things about the new modules:
1. You can import only exactly what you need. You no longer have to import the entire D3 library to use it in your application. This can mean significant savings in download size for your application if you only need a few odds and ends from D3.
2. Because the modules are written in ES6 you can use them just like any other modules if your are using Babel. To me this leads to cleaner code with clear indications of what functionality is being used where in the app. The D3 modules themselves are also much easier to follow if you need to dip into the source code. ES6 modules just make reading code much clearer.
3. These new modules have also addressed problems with data mutation in the layout tools that have caused some pain in the past. Functions like the stack layout, for example, no longer mutate your data. Instead it returns a new object with the layout information indexed for lookup. Avoiding mutation of data makes it easier to integrate D3's layout tools with with the one-way-data-flow/don't-mutate-your-state ecosystem of libraries surrounding React (Redux being prominent among them).
Enter, Update, and Exit in React
In these examples, each node is given a type (entering, updating, or exiting). The components that render these nodes know how to react to these various types. The term "node" is deliberately abstract, it can be applied to your particular use case how ever you want. Looking at the examples nodes can be considered roughly things like paths, bars, and scale ticks. But look closely at the examples. In order to be more efficient with interpolation things are grouped and moved together in ways that made sense for these particular examples. The "bars" are actually the rect itself along with the left hand label (state abbreviation) and it data value label. The "ticks" are the actual numbers themselves and the gridlines that move in coordination with them. This is where some knowledge of SVG and a little bit of ingenuity can make your visualization much more performant. This approach allows you to fine tune these things in a very fine grained way.
A Redux store or parent component keeps track of what nodes are currently mounted and what has been "removed" since the last update. This is where these examples are a little tricky. I am exploiting the fact that when a node is exiting the element should finish having it's opacity turned off or be moved off the screen (which in practice is going to be the case or the node should not be considered to be exiting). It's not ACTUALLY removed from the DOM until the next update (or it enters again). If a node finishes its exit transition it simply notifies the Redux store or the parent container (whatever is keeping track of the mounted and removed nodes) that it has been "removed." On the next update this will be taken into account in deciding the types of all the mounted nodes.