How to build a Radial Bar Chart with D3 JS and React JS
Tags: d3js, dataviz, react, javascript
Published: 2023-10-17 10:05:33

## Technologies
D3 JS is an amazing JavaScript library for building dynamic and interactive data visualization charts on the web. The library works seamlessly with Scalable Vector Graphic (SVG) to render charts. SVG is a file format based on vector image. It allows developers to define two-dimensional graphics and supports animation and interactivity. SVG can be styled with CSS to create aesthetically pleasing charts. 

The project will utilize React js to handle rendering. React is an open-source JavaScript library. It is used to build user interfaces based on components. The library will be crucial in building various UI elements in this chart. 

## What you will learn
- Working with `d3.arc()`
- Building a dynamic chart
- Styling SVG chart

## How to build a project with D3 JS
This project will entail building a complex chart using D3 JS and React JS. Specifically, we will be building a radial bar chart which is a common chart on the dashboard for visualizing KPI's indicators as a percentage. 

![radial chart d3.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jr2g8no18c82o17jpa7f.PNG)
In this project, I opted for a slider with a range of 0 to 100. The slider will simulate a dynamic data source. This is an imitation of a sales dashboard taking data from a database and presenting a conversion rate indicator to the end user. 
React JS will manage the state of the chart. It will monitor changes on the slider and update the data being fed to the chart. The `useState` hook will come in handy in managing state.

The radial chart will have the following elements:
**1. Two arcs**
**2. Text labels**
**3. Slider to stimulate dynamic data**

One arc will act as the background the other as the foreground. The background arc will help visualize the remaining part that is not covered by the first arc. It gives an illusion of background color on the path that the main arc of the chart will follow.

## Setting up the project

- Creating react app 

```javascript
npm create vite@latest radialbarchart -- --template react
```
- Navigate to the folder with the project

```javascript
cd radialbarchart
```
- Install dependencies (_D3 js library in this case_) 
 

```javascript
npm install d3
```
- Run the project using the command
 

```javascript
npm run dev

```
2. Figure out data

D3 charts are data-driven. Visualization of data, make comprehension of trends and patterns from users. 
In our case, input values will come from the slider. The slider will simulate value changes based on the provided range. 
React Js will keep the chart and slider value in sync. This is achieved by the `useState` hook to maintain the state.

**Managing state**
```javascript
import { useEffect, useState, useRef } from react';
 
const [sliderValue, setSliderValue] = useState(80)
 
function handleChange(e){
      setSliderValue(e.target.value)
}

```
**HTML Slider (input data)**
```javascript
  <div>
       <div>{sliderValue}</div>
        <div className="slidecontainer">
          <input 
          type="range" 
          min="0" 
          max="100"  
          value={sliderValue}  
          onChange={handleChange}/>
        </div>
    </div>
```
 **Handling re-rendering**
The code for drawing the entire chart will be encapsulated in a single function named drawChart. The `useEffect` hook will help re-render that chart every time the sliderValue changes.

```javascript
useEffect(() => {
    drawChart(sliderValue)
}, [sliderValue])

function drawChart(value){
  //code for drawing chart
}
```

## Drawing and setting up SVG element

- The `height` and `width` values will be used to set the size of the viewBox attribute on the SVG.
- `Tau` represents the whole arc, a complete arc is 360 degrees which is equivalent to `2 * Math.PI`
- `maxValue` will prove useful when converting values to percentages

```javascript
function drawChart(value){
     const height = 350, width=960;
     const tau = 2 * Math.PI;
     const maxValue = 100;
     const slice = value/maxValue

   //other code for drawing chart will go here
}
```
**Prepare DOM element**
The next step is selecting the element that we will hook our charts elements. React js provides `useRef` hook to attach the SVG element on the div. Note that we set `height` and `width` as 100% and make use of the viewBox property. This ensures that our SVG chart is responsive to different screen sizes.
We attach a `g` element which is equivalent to a `div` in HTML to group elements together. This way it is easier to move a group of elements on an SVG element. The `g` element is transformed to the center of the SVG element by the transform property.

```javascript
const containerRef = useRef(null)
```
```html
  <div className='container'>
    <div ref={containerRef}></div>
  <di>
```

```javascript
select("svg").remove() 
const svg = select(containerRef.current)
           .append('svg')
           .attr('height', '60%')
           .attr('width', '100%')
           .attr("viewBox", `0 0 ${width} ${height}`)
           .append("g")
           .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")

```
Before drawing the SVG chart, it is essential to remove any SVG element from the previous render. This is achieved by `remove()` function.

## Drawing arcs
An arc is a curve, but in mathematics and computing an arc is a section of a circle. D3 provides utility functions to draw arcs commonly known as arc generators.
In this case, we have an arc generator called `arcGen`. It will handle the creation of arcs. The `arcGen` generates an SVG path based on the provided data value.
D3 library helps a developer make complex shapes through shape generators. The `d3.arc()` returns a function that generates arcs based on provided `end angle`. It takes input value from the slider and updates the chart.

```javascript
const  innerRadius= 90, outerRadius= 120, 
startAngle= 0, cornerRadius= 40;
 
const arcGen = arc()
    .innerRadius(innerRadius)
    .outerRadius(outerRadius)
    .startAngle(startAngle)
    .cornerRadius(cornerRadius)
```
 
This radial chart has two arcs namely `arc1` and `foreground`.

```javascript
   const arc1 = svg.append("path")
          .datum({endAngle: tau})
          .style("fill", "#ddd")
          .attr("d", arcGen);
 
   const foreground = svg.append("path")
          .datum({endAngle: slice * tau})
          .attr('fill', '#F57B21')
          .attr("d", arcGen);
```
 
 
1. The `arc1` arc has a very light shade of gray and takes an object `endAngle` of `2*Math.PI`.
2. The `foreground` arc is proportional to the input value of sales. Providing a value of 100% from the slider will be a donut chart. The foreground arc will have an orange color which is easily visible to users.

## Adding text labels on the chart
Labels make charts easier to understand. 

```javascript
  svg.append("text")
          .attr("text-anchor", "middle")
          .text(`${value}%`)
          .style('font-size', '3.2em')
          .style('fill', '#A9BF51');
```
     
The text value is attached to the SVG element and styled using CSS. The text value also pumped up to have `font-size` of `3.2em` and a `color` value of `'#A9BF51'`.
CSS style text-anchor with the value of middle will center text value horizontally. The chart will have text in the arc to show the value visualized by the arc.
 

```javascript
   svg.append("text")
      .attr("text-anchor", "middle")
      .text("Sales")
      .attr('dy', '1.45em')
      .style('font-size', '1.75em');
```
 
The label value is moved horizontally using `dy` attribute by `1.45em` and gets a smaller `font-size`of `1.75em`.
 
## Final Code
Code: [CODE](https://github.com/simbamkenya/radial-barchart-tutorial)
Live chart: [LIVE CHART](https://sales-radialbarchart.netlify.app/)

Here is the final code:

```javascript
import { select, arc} from 'd3'
import './App.css';
import { useEffect, useState, useRef } from 'react';

function App() {
  const [sliderValue, setSliderValue] = useState(80)
  const containerRef = useRef(null)

  useEffect(() => {
    drawChart(sliderValue)
  }, [sliderValue])

    function handleChange(e){
      setSliderValue(e.target.value)
    }

    function drawChart(value){
      const height = 350, width=960;
      const tau = 2 * Math.PI;
      const maxValue = 100;
      const slice = value/maxValue
      const  innerRadius= 90, outerRadius= 120, startAngle= 0, cornerRadius= 40;

      select("svg").remove()

      const svg = select(containerRef.current)
                  .append('svg')
                  .attr('height', '60%')
                  .attr('width', '100%')
                  .attr("viewBox", `0 0 ${width} ${height}`)
                  .append("g")
                  .attr("transform", "translate(" + width / 2
                  + "," + height / 2 + ")")

        // An arc will be created
      const arcGen = arc()
          .innerRadius(innerRadius)
          .outerRadius(outerRadius)
          .startAngle(startAngle)
          .cornerRadius(cornerRadius)

      const arc1 = svg.append("path")
          .datum({endAngle: tau})
          .style("fill", "#ddd")
          .attr("d", arcGen);


      const foreground = svg.append("path")
          .datum({endAngle: slice * tau})
          .attr('fill', '#F57B21')
          .attr("d", arcGen);

      svg.append("text")
          .attr("text-anchor", "middle")
          .text(`${value}%`)
          .style('font-size', '3.2em')
          .style('fill', '#A9BF51');

      svg.append("text")
          .attr("text-anchor", "middle")
          .text("Sales")
          .attr('dy', '1.45em')
          .style('font-size', '1.75em');
      }


  return (
    <div className='container'>
        <div ref={containerRef}></div>
        <div className="slidecontainer"> 
          <div className='salesfigure'>{sliderValue}</div>
          <input 
            type="range" 
            min="0"
            max="100" 
            value={sliderValue} 
            onChange={handleChange}
          />
        </div>
    </div>
  );
}

export default App;
 
```

##Conclusion
A radial chart is an interesting and useful chart. D3 js utilized `d3.arc()` to create the charts. This is a complex chart that is broken down into three parts, foreground, background, and slider.

I am open to freelancing work on D3 and data visualization. Contact me on X [simbamkenya ](https://twitter.com/SimbaMkenya)

Thank you for your time