Back to Blog

React meets D3

24 Oct 2016

Ok, so here is a problem you might encounter when it comes to create a D3 graph in a SPA application:

I have a React app and I want to throw some graphs in. But I don’t know D3 pretty much and wrapping-ready-to-use-charts libraries don’t meet my needs. React is a great tool for my work, I know it very well so I can go quite fast with it. So maybe should I create graphs in a React directly? Eventually just reuse some basic parts in D3 like scales and forget about the rest?

I had the same a while ago. You will find at least several propositions over Internet how to attack this one. Below my two cents.

Let’s make things clear at the beginning about some wording. I’m using framework and library interchangebly to make things simple, as usual. Strictly speaking, if you use definitions that library is a code your code calls and framework is a code that calls yours, we should say that React is a framework and D3 is a library, maybe. But it doesn’t matter here, because both of them are tools that are used for the same thing: to render markup on the browser. And they are compared here in this context.

React and D3 have some similarities: both utilize one directional binding and rely on functional concepts, both are more markup-generation engines than full-featured frontend frameworks like, Angular. Nevertheless, the problem is, these are two libraries that doesn’t fit very well.

And this is because both libraries, although somewhat similar, solve two totally different problems:

  1. React is about taming complexity of the frontend state, by limiting its scope to small components or eliminating it altogether. That’s it. My personal definition of React. This is the main benefit, killer feature, main reason why writing apps in React makes it so simple, cheap, easy, testable and predictable. Besides that, React arranges all components in a tree structure which is natural layout for user interfaces and, of course, HTML markup itself. (By the way, I cannot understand why someone still might want to sort this out as “directives”, ServiceFactoryProviders, ServiceFactoryFactoryProvidersStrategies and all this “enterprise patterns” nonsense. Been there. If you do it, good luck. You are gonna need it.)

  2. D3, besides its rendering features, is mostly about calculating coordinates for visualization elements. It provides math functions for scaling, animations, common primities like axes or even ready to use so called layouts, which map your data right into coordinates (like words cloud layout). Yes you may borrow only some parts of D3 and put them into other frameworks (like React) directly, but you will loose its whole power if D3 is not utilized in an “idiomatic” way. Secondly, visualization is something you rarely think about as a tree of components. You may try to think about it as a group of layers or something similar. We still have to divide and conquer, group reusable components and organize some hierarchy over them, but it is rarely strictly a tree of nested visual elements like in regular UI. Grammar of graphics is an example of such standarization. It only shows that this domain has its own standards and rules which are somehow orthogonal to HTML/CSS/JS world.

Those points bring us to the conclusion that when it comes to the problem of adding graphs to a page, writing them in React gives a little value, because it is not the problem React tries to solve. You don’t need to be afraid of a state in graphs. Sure, it does exist if you have interactive ones. But comparing it with the state for SPA application forms, business logic complexity and fluctuation, their validation rules, routing, all the pages flow, calling api, handlig errors…, it’s a piece of cake.

In other words, just use right tool for a job: D3 for viz and React for the rest of an app. I did this for you and I tried other way, belive me. It just doesn’t work.

Solution

So how do I do it? Just render native DOM in D3 when React component is mounted and that’s it. With plain old Javascript it might look like this:

  var Container = React.createClass({
    componentDidMount: function(){
        renderGraph(this.refs['container'], this.props.data);
    },
    render: function() {
      return React.createElement('div', {ref:'container'});
    }
  })

  var renderGraph = function(container,data){
    d3.select(container)
      .selectAll('h2')
      .data(data)
      .enter()
      .append('h2')
      .text(function(x){return x})
      .style('color', 'red')
      .transition()
      .style('color', 'blue').delay(1000);
  }

  ReactDOM.render(
    React.createElement(Container, {data:[1,2,3,4]}),
    document.getElementById('container')
  );

And that’s all about it. Full example is here.