Nice axis labels for React recharts using D3


Imagine you are creating a chart, but you don't know what the time period is going to, nor how many observations there will be. Figuring out the axes scales,  based on the data presented, can be labor intensive, especially if one of the scales is time. 

The DOM and React

Normally for charts like these I would use either Google Charts or D3, both of which are pretty good at figuring out good axis scales, but these APIS can be problematic when used with React. React manages the DOM by keeping a virtual representation of it and its that virtual DOM layer that gets updated by React components, and only changes are committed to the actual DOM by some React flushing process. Google Charts and D3 both want to manipulate the DOM directly, so I figured this would likely screw up React. Luckily I came across Recharts, which uses D3 under the covers but presents as React components, and they have presumably solved the problem of DOM confusion, so I decided to have a go with that.

Recharts

It doesn't have an automatic way of adjusting a time scale, and what I wanted was sensible axis labels depending on the period of the chart. 

For example

versus

Recharts does have a pretty good attempt at deciding which labels will fit, but it doesn't automatically change their scale formatting in the way you can with D3. So i thought maybe they could be combined - like this.

The xAxis component in Recharts is this
          < XAxis 
            dataKey = "start"
            tickFormatter = {tickFormat}
          /> 

and the yAxis
          < YAxis 
            label ={"Volume"}
            yAxisId = "right"
            orientation = "right"
            stroke = "#9E9E9E" 
            tickFormatter = {kbTickFormat}
          / >

and the formatting of the labels has been delegated to functions created by d3.

get the d3 domain, so we can make nice labels
      const domain = d3Extent (slotData, d=>new Date(d.start)); 
      const tScale = d3ScaleTime().domain(domain).range([0, 1]);
      const tickFormat = tScale.tickFormat();

      
do the same thing for the yaxis scale
      const kbDomain = [0, d=>d3Max([d.setsize,d.getsize])];
      const kbScale = d3ScaleLinear().domain(kbDomain).range([0, 1]);
      const kbTickFormat = kbScale.tickFormat(5,d3Format(".1f"));

 I'm using d3 v4, which you probably are too if you are using React. You don't need all of D3, so this will bring in enough for the code above.
import { extent as d3Extent, max as d3Max } from 'd3-array';
import { scaleLinear as d3ScaleLinear, scaleTime as d3ScaleTime} from 'd3-scale';
import { format as d3Format } from 'd3-format';


And that's it - all the goodness of D3 without interfering with React.





For more like this, see React, redux, redis, material-UI and firebase. Why not join our forum, follow the blog or follow me on twitter to ensure you get updates when they are available.
Comments