Time Series Charts with React, Redux and D3

Time Series Charts with #ReactJS, Redux and D3:  by @byrichardpowell #JavaScript

  • We have just finished a complete rewrite of our time series charts using React, D3 and Redux.
  • This way, we soon had control over the y axis, 2 render types and support for multiple axis:

    By the end of my second random week we had stacked area charts, tooltips, y axis with dynamic widths, colour control, axis that were entirely optional and the presentation was a lot more polished:

    This progress was encouraging but it was deceptive.

  • The architecture of the new charts loosely follows 3 statements: React for composability, Redux for performance, helpers wrapping D3 for data visualisation heavy lifting.
  • This pattern is certainly not unique to our time series charts, in-fact it’s pretty common to React and Redux applications.
  • What is interesting though is the properties our chart tooltips have that make this pattern so useful, specifically:

    The next time you have a performance issue with React, perhaps you might consider the above 4 points and arrive at the conclusion that Container components are the way forward.

How we rebuilt our time series graphs using React, Redux and D3 to create server monitoring graphs as reusable components.

@HappyFunCorp: Time Series Charts with #ReactJS, Redux and D3: by @byrichardpowell #JavaScript

For 2 years Server Density used Backbone and Rickshaw to render server and website monitoring data into time-series charts. Rickshaw is a great library but we had to write a lot of code to bend it to our requirements. Time was not kind to that code. The codebase migrated towards React from Backbone and then from Backbone to Redux, but this code remained static and became an area we were hesitant to touch. Every codebase has these areas, unfortunately for us ours was the time series code.

We have just finished a complete rewrite of our time series charts using React, D3 and Redux. Now this has been released to customers, I’m going share how we developed this code and its high level architecture. If you like React, Redux, Data Visualisation, architectural challenges or if you want insight on approaching a large re-write then read on!

Server Density’s time series charts have pretty well-defined requirements:

These were requirements that absolutely had to be met but I had some soft requirements in mind too. Mainly, I wanted to make sure that our developers would not have to worry about complexities like layout and scales. Instead I wanted our developers only concerns to be the helper functions being used, the props specified and any components composed.

So that we could be sure what we developed worked in production, I grabbed a sample of the time series data that our charts render. This way we could plug the new charts into the existing data layer and limit the refactor to the view layer.

To develop the charts inside our application without worrying about keeping pages up to date with master, I added a test page. Gradually we would add multiple examples, and requirements were fulfilled.

(Easter Egg: This page still exists and can be viewed by manually browsing to /react-charts-test-page when you log in to your account).

At Server Density all our engineers get a random week every 2-3 months. This is a week where we can work on anything we like, so long as it has some link to the product or company. I chose to work on time series charts, which is how this project to build a core part of our product started out. Each week I considered the requirements and fleshed out the architecture. This way, we soon had control over the y axis, 2 render types and support for multiple axis:

By the end of my second random week we had stacked area charts, tooltips, y axis with dynamic widths, colour control, axis that were entirely optional and the presentation was a lot more polished:

This progress was encouraging but it was deceptive. 80% of functionality existed, but that only took 20% of the development time. The remaining 80% would be spent on additional tooltip functionality, separating component concerns, unit tests and architecture refactors. Out of all of this it was the architecture refactors that took the most time, but I think it was worth it.

The architecture of the new charts loosely follows 3 statements: React for composability, Redux for performance, helpers wrapping D3 for data visualisation heavy lifting. I’m going to discuss how React & Redux fit into the architecture now.

Very quickly we saw that multiple composable, declarative components made it easy to specify a chart’s configuration. But what does this mean?

Consider the following spark-line example:

In this example the chart is 600 pixels wide and 100 pixels high and it renders a line chart. Now let’s look at an example for a chart with multiple axis:

This chart will also be 600 pixels wide and 100 pixels tall. It will render a line chart, has an x axis and multiple y axis. If that explanation was not needed then I believe the component API is declarative.

This architecture has advantages beyond simple configuration:

By default React has quite a simple mental model. Data down, actions up, if something changes re-render everything below it. Whilst this works very well 90% of the time, there are situations when it won’t be fast enough. We had one of those situations, which is demonstrated in this GIF:

In this GIF each chart responds to a mouse event on every other chart by updating the tooltip. Unfortunately, because every mouse event would re-render every chart component, large data sets incurred a noticeable lag. That’s because we were using an architecture like so:

Fortunately, React-Redux provides a convenient way to connect state to specific components bypassing the component hierarchy entirely: containers. Instead of using setState above the chart components, we could use Redux to update the store state. React-Redux connect could ensure that only the components that had to update would receive new props. The new architecture would look something like this:

The important point is that only one component renders when the event or action occurs.

This pattern is certainly not unique to our time series charts, in-fact it’s pretty common to React and Redux applications. What is interesting though is the properties our chart tooltips have that make this pattern so useful, specifically:

The next time you have a performance issue with React, perhaps you might consider the above 4 points and arrive at the conclusion that Container components are the way forward.

From a developer’s perspective React, Redux and D3 is a match made in heaven. React gives you easy composability, Redux gives you easy and performant state management and D3 excels at the data visualisation heavy lifting. These 3 tools have allowed us not only to fulfil the requirements, but to establish an architecture that has made previously complex functionality quite trivial. For example highlighting when data is missing:

Or improving accessibility for color blind users whilst also making it easier to track a series on a busy chart:

But have users noticed the difference? Yes, in many ways. My fondest memory in this respect is whilst we were testing the new charts internally, our team only wanted to look at the new charts and not the old ones, commenting on the many improvements. We also know of customers who appreciate the extra visual polish and the extra robustness around interactions. We know which customers reported issues the new charts have now fixed.

The big “coming soon!” headline has to be drag to zoom (which benefits greatly from the architecture described above):

We know customers would like to see new render types and more control over the y axis. Personally I’d also like to overlay alert data onto charts. But I’m most interested in what you would like to see, so please email, tweet, or comment below.

I am working to open source this code, but it will take some time. It’s tricky extracting code like this from a large codebase and it would be no use without good documentation and examples. If you’d like to see this code then please email, tweet or comment below.

Time Series Charts with React, Redux and D3