How to create a pie chart with D3.js and React Hooks (Part 1)

Kihwan Cho
Station Five
Published in
5 min readJan 29, 2019

D3.js is one of the most popular JavaScript libraries for data visualization and is used widely with React. When creating these visualizations using D3 and React, the component often needs to have internal state and as a result are Class components. It is very difficult to have these graphs as functional components.

However, with the introduction of Hooks and supporting SVC elements, we now have a way to create these visualizations as functional components and no longer have to deal with Class components (down with classes)!

The purpose of this blog post is to show you how much simpler (and easier) it is to create graphs with D3 using React Hooks instead of Class Components.

*** Spoiler Alert ***

The code is reduced by more than half!

In this article, we are going to describe how to

  • Install React 16.8.0 alpha 1, d3 and react-spring
  • Generate a function which can generate random data
  • Create a pie chart with an update function using React class
  • Create a pie chart with an update function using Hooks
  • Replace d3 functions with SVG elements
  • Improve a chart with attributes

In the next article, the following items will be done.

  • Cache the previous value to add animations using useEffect
  • Add animations using d3 transition functions
  • Add animations using react-spring

What we will be building

If you want to have a play with the pie graphs before reading, here’s the live example. Please do not eat :)

Getting started

First of all, let’s install the Create React App 2.0, simply running the command below.

npx create-react-app my-appcd my-app

As we want to use Hooks which haven’t been officially released at this moment, we need to update react and react-dom to 16.8.0-alpha.1

yarn add react@next react-dom@next

And we also need d3 and react-spring.

yarn add d3 react-spring

index.js

We are going to create a function that can generate random data in index.js. generateData function will randomise data every time we click a button. Let’s take a look at the code below.

Oh look, our first hook! We use the useState hook and generateData function to generate the random data and store it as the state variable data. The useState hook is used to manage local state in function components.

Then every time the button is clicked, it will run the changeData function where it will change the value of data through the setData function.

If you are still unsure of the useState hook please refer to the React docs or this article which has a more in depth explanation.

This data variable is passed to the 3 components below.

  1. PieClass → A class component that will render a Pie Graph
  2. PieHooks → A functional component using hooks that will render a Pie Graph
  3. PieSVG → A Pie Graph using just SVG elements (supported since React 16.6)

Pie chart using React Class

Let’s have a look at the Pie Chart using Class Components.

If you looked at the code above and thought “well the componentDidMount and componentWillUpdate(or componentDidUpdate) look awfully similar.” Then 10 points for you! The code is almost identical and I don’t know about you but I’d rather not have to repeat my code.

Let’s look at what each of them are doing. The code in componentDidMount will render an initial state for the pie graph, and the code in componentWillUpdate will update the nodes instead of appending the nodes again.

In both lifecycle methods we take advantage of the d3 methods assigned in the constructor.

  • d3.pie() takes an array of data and will generate an array of objects which contains startAngle, endAngle, data, index and so on.
  • d3.arc() takes innerRadius, outerRadius, startAngle and endAngle and will return a string value of coordinates of each item which can be used in <path> object. So, this can consume data generated from d3.pie().
  • arc.centroid() function will return x, y values which mean the centre of each arc.

Although this renders the pie graph, to me it is annoying that we need to have a componentDidMount to handle the initial state and a componentWillUpdate to handle any data that has updated. The code is almost identical in both lifecycles and if you are not aware of the exit, remove and merge pattern it is also quite confusing as to how we handle updated data for the graph.

Pie chart using React Hooks

In this step, we will avoid code duplication using Hooks.

So how do we solve the problem above? Well with hooks of course! Using hooks we can replace the class component with a functional component.

To avoid code duplication, we will look at using the useEffect hook. The useEffect hook comes into play for lifecycle methods / side-effects which are usually used for interactions with the Browser/DOM API.

The useEffect hook will take responsibility for both componentDidMount and componentDidUpdate so we can avoid code duplication as codes below.

So how does it encapsulate both componentDidMount and componentDidUpdate?

When you use the useEffect hook you are telling React that your component needs to do something after render. React will remember the function you passed and will call it after the DOM updates.

You can see that in the second argument of useEffect we pass an array with the value [props.data] . We are saying that every time the value of [props.data] changes run the function we passed. If it does not change, do nothing!

The other hook is the useRef this hook replicates what React.CreateRef() does and provides us a way to access DOM nodes. This allows us to target the <g> in our useEffect.

You can see compared to the class component, the code is more concise, easier to understand and no duplication!

The simplest way using SVG elements

In this step, to reduce mutations, we try to use D3 as sparingly as possible.

Instead of using append() and attr() functions from D3, we can simply map over the data and return SVG elements with its attributes as <path d=“…” … />. In this case, readability can be increased with less state mutations. We don’t even need Hooks for now. (Please note that Hooks will be used when we apply animations later on)

You can see that we just pass the functions and data to the <Arc> component which is just SVG elements. We’ve eliminated the use of append() and attr() functions from D3.

Class vs Hooks vs SVG Elements Difference

Class Component vs Functional Component (using hooks) vs SVG Elements

As you can see from the images above, the functional components using Hooks and the functional component just using SVG elements is significantly less than the class component. Not only that, it is a lot nicer to read and understand. Please note that for Part 2 we will be using the functional component using Hooks since we want to introduce animations!

Conclusion

Thank you for reading this post. We hope this article helps you to understand how D3.js can be implemented in the React world. Next time, we will add some animations with and without using d3.transition based on what we made in this article. Watch this space!

--

--