React + d3.js

I bought the pre-sale of Swizec Teller's React+d3v4 book to learn more about creating interactive page elements with React and d3.js.

D3 is great at data manipulation and visualization but terrible for DOM manipulation. React is excellent for DOM manipulation and rendering optimizations. To combine those two, we let React own the DOM and use D3 for mathematical and visualization functions.

We can embed d3 and other non-React libraries into React in two ways, through blackbox components and higher order components.

Blackbox Components

This means using React as a thin wrapper around a d3 visualization. React renders an anchor element, and d3 attaches it's visualization to that element. Here's what a generalized Blackbox component would look like:

import React, { Component } from 'react'

/*
	A higher order function to create a react wrapper
	around d3 rendered elements.
*/
export default function D3blackbox(D3render) {
  return class Blackbox extends Component {
    componentDidMount() {
      D3render.call(this)
    }

    componentDidUpdate() {
      D3render.call(this)
    }

    render() {
      const { x, y } = this.props
      return <g transform={`translate(${x}, ${y})`} ref="anchor" />
    }
  }
}

All React does is create the DOM and position the visualization. The consumer of this HOC implements their visualization in the D3render function. Rendering of d3 axes is a great use of the blackbox method because axes are hard to get right on your own. Here's how simple it is:

import * as d3 from 'd3'
import D3blackbox from '../D3blackbox'

/*
  Axis passes it's own rendering function to our D3blackbox HOC.
*/
const Axis = D3blackbox(function() {
  const axis = d3
    .axisLeft()
    .tickFormat(d => `${d3.format('.2s')(d)}`)
    .scale(this.props.scale)
    .ticks(this.props.data.length)

  d3.select(this.refs.anchor).call(axis)
})

This is okay for small components but it's best to use it sparingly because you have to handle re-render prop and state changes manually. For more complex uses of d3, we can use what Swizec calls full-feature integration.

Full Feature Components

Full feature components leverage React's rendering engine to display SVGs. So, React renders and D3 calculates the props. This is a three part pattern:

  1. Set up D3 objects as class properties
  2. Update D3 objects when the component updates
  3. Output SVG markup in render()

I put together an example on CodeSandbox of a full feature component implementation, see it here. The screenshot below shows the end result.

D3+React visualization.

The combination d3+React leads to super composable visualizations. With them we now have the ability to easily combine visualizations in complex ways, all while keeping the rendering and data handling easy to manage.

Thanks for reading.