D3 + React Intro — Part 2: Fun Multi Layered Donut Chart

Scott Hunt
principledminds
Published in
5 min readJun 13, 2018

For this post I’ve built a cool little two layered donut chart with some hover functionality (CodeSandbox included at very bottom). While it may seem like this is a big step from the Simple Pie Chart I built in Part 1, you should be familiar with most of the patterns you’re going to see in the code, assuming you’ve worked a bit in React already. The additional complexity on the D3 side that I’m introducing here should be rather manageable.

I hope you will see that by merely stringing together simple logic enough times, we can create visualizations that look deceivingly more advanced than they are.

Funky two layered donut chart with hover effect

Here’s a screenshot of the two layered donut chart we’re going to build.

As we move the mouse over the inner slices, we activate a nice hover effect, which increases the size of the inner and outer slices, to create a ‘grow’ effect. In addition, we fill the center of the donut on hover with a circle

Step by Step Breakdown

Much of the early code is exactly like it was in the Simple Pie Chart example that I showed in Part 1 of this intro series (see here).

Thus, I won’t waste your precious time going back over those lines. After all, I know you’re busy and want to get to it!

— — —

We kick off with the Path component that is responsible for returning our <path> elements that will render the slices. To be able to enjoy our mouseover effects, we will take advantage of React’s built in setState() method to change the state of our component when the cursor moves over the inner circle element.

If this pattern is new to you or confuses you, I would recommend brushing up on your basics in React.js, as setting state in a functional manner is arguably one of the most used patterns you’ll run into in React and you should get comfy with it!

Ok, if you are initially confused by the next code block, don’t despair. Seeing D3’s various functions are always scarier than they really are.

My tip: Console log the shit out of D3 functions’ return values! Seeing what data comes back under the hood will help you turn total mystery into a clear understanding.

So… let’s dissect what’s going on here:

  1. We use the ‘radius’ property that we passed in the parent component as props (see Codesandbox at bottom for entire code) to define our outer radius and inner radius. Here’s where our ‘isHovered’ state property comes into play for the first time → We increase the radius of the outer donut by 0.1 (10%) if ‘isHovered’ is TRUE.
  2. Next, we define the arc function (which we’ll need to draw the paths for the slices) for the inner donut.
  3. Now, we have to create our arc function for the outer donut, which I called ‘arc2’ here (I know, I know. My variable naming sucks). However, note that in the code for the Simple Pie Chart, we called D3’s pie() function with our data to generate an array called ‘pie’ very early on… Well, you guessed it, we have to do that for our outer donut, as well, else our arc2 won’t have any sensible data to draw.
  • Notice, that our outerPie is getting some additional function calls tagged on, namely: ‘startAngle’ and ‘endAngle’. Basically, by default pie() will generate values for your data with the assumption that you’re going to want a full circle (i.e. 360 degrees or ~6.28 radians). However, we of course only want to display data for the outer donut on top of one piece of the inner chart. Therefore, we need to tell D3: “Hey, whatever data I’m going to pass to you, instead of making a full circle, make the slices proportional to the inner slice they are associated with.

Ok… still with me here?

Again, I urge you to simple console log the ‘pie()’ and ‘arc()’ functions with test data so you get a feel for what they are doing. Once you see how the two are inextricably linked to one another and build your pie chart slices based on the info ‘pie()’ passes to ‘arc()’, you’ll see how straightforward this whole process actually is.

4. Finally, now that our outerPie is made, we create our arc2 that will eventually be passed its slices.

Last but not least, we have to return our JSX elements so that our entire efforts can actually be seen!

  1. What will help is to remember, that this code sits in the ‘Slice’ Component, which is currently being mapped multiple times by our parent component. Thus, the first <path> element you see is going to be responsible for the slices of the inner donut. Here’s also, where we will pass our ‘onMouseOver’ and ‘onMouseOut’ functions along so we get the desired hover functionality.
  2. Now that our inner slice has been taken care of, we have to render our outer slices for that particular inner slice. We’ll follow the same pattern we used in the parent component (see full code in Codesandbox below). Note: We are using ‘arc2’ here and passing our array values that are being generated by calling ‘outerPie’ on our ‘this.dataOuter’ data set.
  3. The last piece is our beautiful little circle in the middle to round things off… pun intended :D

I really hope this was a helpful write up. Please feel free to let me know how I can make the information clearer going forward in the comment section.

Scott

PS: Here’s the full code:

--

--