36

Customizing Chart.JS in React

 5 years ago
source link: https://www.tuicool.com/articles/hit/qmEri2a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Customizing Chart.JS in React

bQVjIf6.jpg!webAZr22eb.jpg!web
Photo by Isaac Smith on  Unsplash

ChartJS should be a top contender for any data visualization endeavor in React (and in other frameworks). While D3 is a power house of possibilities, and other worthwhile options exist — ChartJS solves most data visualization needs.

What about using the React-ChartJS-2 Wrapper?

If you are a React developer exploring ChartJS — chances are you’ve found the really friendly and easy to use wrapper . If you have a simple dataset with few customization tweaks, it’s a great solution. However, as your data visualization and customization needs grow — the more you’ll benefit from getting under the hood of ChartJS. We can refer directly to the ChartJS documentation and customize as much as we need.

Using ChartJS in React

I’ll be walking you through my implementation and customization of ChartJS in React: addressing the initial set up, common customizations, and working with custom tooltips. By the end of this article, you should feel comfortable working with ChartJS and implementing a custom design in a React project.

:rocket: Initial Setup

You can set up your own boilerplate for the project however you’d like. The main focus here is the component where we are creating and customizing a chart.

Following Along

You can clone down my repo if you’d like to skip the tedious boilerplate work and get to the good stuff. There are two main folders: views and components. Each folder has my code (./ Dashboard ) and a folder for your own work (./ MyDashboard ).

Incorporating into Your Own Project

I provide extensive gists that make it easier to incorporate into your own project. The only additional step you’d need to take is to install ChartJS:

npm install chart.js --save

Line charts are, in my opinion, the most popular way of displaying data. So I’ll focus on working with line charts. However, once you’re familiar with this process, it isn’t that challenging to walk through the docs and figure out further customizations in a different chart type. You’ll want to create a new class component named myLineGraph.js ( change your component name to myLineGraph instead of LineGraph ) with the following snippet:

EVJFFrM.jpg!web

You’ll frequently see ctx as the variable assignment in other tutorials and the documentation. For instance:

var ctx = document.getElementById('myChart').getContext('2d');

Here, I am taking advantage of refs in React and passing in our <canvas/> as the first argument to our new Chart. Both will work, but my React approach will differ from the documentation. No need to be scared, though.

If we place the new Chart() outside of a lifecycle method, our component won’t be able to find the <canvas/> since render() will not have executed yet. There are a few different ways of handling this, but we’ll start with this.

Styling the Canvas Element: You’ll notice that I don’t pass styling to the canvas element. Instead I apply styles to an outer div container. I have found through trial and error it is much easier to style a container div than the canvas itself. You’ll save yourself a lot of headache if you can manage styles from an outer container.

Side Note about CSS Modules

This line of code might look unfamiliar to you:

import classes from "./LineGraph.module.css";

If you’re not familiar with CSS Modules, they now come out of the box in CRA. Previously you had to eject your React App and edit the webpack config. Now it’s easier than ever to scope your CSS. Simply renaming your CSS files with the .module.css will allow you to start using CSS modules. The imported classes will be an object which you can use to assign styles. No more collision with styling from other stylesheets. If you inspect the className on the div holding the canvas you should see a unique name like this:

<div class="LineGraph_graphContainer__2g04_">

Here’s what we’re starting out with:

M3AvEbm.png!webJr2iean.png!web
Initial Chart

Without any fancy work on our part, we have a load animation, a tooltip, a legend, and axes labels. We’ve already accomplished quite a bit without much effort. But now we need to populate and pass down relevant data. For the sake of practicality, we’ll be using mock sales data from the mockData.js file.

Passing Data

Now let’s pass in the provided mock data in the Dashboard component:

EVJFFrM.jpg!web

This is starting to look a lot more useful than before.

vuMFb2Z.png!webbmyMFnJ.png!web
With Annual Data

Working with Multiple Datasets

The datasets key is an array. So we can easily add another object to the array with a national average or any other data. We have an exported nationalAverageData array that you can pull in from the mockData.js file to add another dataset.

EVJFFrM.jpg!web
2yUnYbM.png!webqAB32iR.png!web
Team Sales vs National Average | You can toggle the datasets by clicking on the key in the legend

Protecting Yourself from Label Errors

We have to pass the same amount of labels as we do data points for our graph. Otherwise our graph will be in bad shape. It’s a rare scenario that this would occur, but I prefer to add a layer of protection. Optionally, you could do the same for the data points being passed down. But that can be tricky to do without manipulating the data we want to represent.

EVJFFrM.jpg!web
Displays graph data without issue despite not having labels

Chart Customization

Now, we’ll customize our chart with some of the more popular customization options.

Above our exported myLineChart.js we can add some global default customizations:

EVJFFrM.jpg!web

Adding A Custom Font:

Chart.defaults.global.defaultFontFamily = "'PT Sans', sans-serif"

Hiding Chart Legend:

Chart.defaults.global.legend.display = false;

The legend allows us to toggle visibility of multiple datasets without having to manage state or write a single line of code. However, it can sometimes be helpful to remove for single datasets where the legend isn’t as necessary.

Removing the Line Graph Tension

Chart.defaults.global.elements.line.tension = 0;

Setting the tension to 0 removes the smooth curves in our graph. Optionally, you can set the value to 0.1 or 0.2 to get a slight tension curve.

Example of 0 tension:

b6VRf2E.png!webuYRFVnn.png!web
No Legend, Custom Font, and 0 Line Tension

Responsive Design

EVJFFrM.jpg!web

We can alter our chart to be responsive, and decide whether or not to maintain aspect ratio. Setting maintainAspectRatio to false can be helpful for visually adapting your chart for mobile design. I find in most cases, the above options fit my styling needs. The chart can get “squished” by not maintaining the aspect ratio. So keep that in mind.

Customize the Layout

EVJFFrM.jpg!web
Adjust Padding for a Given Chart

As far as the docs show, padding is the only option here we can pass into the layout object.

Removing Data Ticks, Graph Lines, and Borders

EVJFFrM.jpg!web
em26Nb6.png!webmquu2ur.png!web
All labels, grid lines, and borders removed with tension set to 0

It might be helpful to maintain grid lines or axes labels — but there are instances where the graph representation needs to be simplified. In our case where we’re comparing team sales across a given year, the labels are quite helpful.

Removing Fills

We can alter the line color, and the fill underneath the line. We’ll apply a gradient to the line and the fill of our dataset. For now, let’s see how we can customize the fill.

eieuEn2.png!webiQjMN3F.png!web

That already looks cleaner! Let’s see how we got here:

EVJFFrM.jpg!web

We can choose whether or not to fill a background color underneath our graph line, as well as pass a color for each dataset line.

Gradients

To create a linear gradient, we do the following:

let gradientLine = canvas.createLinearGradient(x0, y0, x1, y1);

The createLinearGradient() is a method on the canvas. It has nothing to do with ChartJS and is applicable to any <canvas/> element.

EVJFFrM.jpg!web

The createLinearGradient() method requires 4 arguments: x0, y0, x1, and y1. These values have to be finite . Which means we don’t have a way of setting a gradient from 0% to 100%. Instead, we have to dynamically find the width of our graph. The first line in the above gist grabs the width from the canvas element and assigns it to chartWidth. We pass the chartWidth in as our x1 value. The same is possible for height if you wish to alter your gradient vertically.

n2eYn2i.png!webvIzU3aa.png!web
Horizontal Gradient

We can apply vertical gradients, add multiple color stops, and use rgb to adjust opacity:

EVJFFrM.jpg!web
3a6zMvb.png!weby6BbUzb.png!web

Vertical gradients are more challenging because our datasets can vary. Which means that we have to adjust our y0 and y1 to be a visible part of the fill or border color. As you can see, I’m having a lot of fun with bold colors. But it’s easy to play with gradients/opacity within a more neutral color scheme to achieve the same thing.

Dynamic Data

We’ve passed data to our component, but what if our clients want to see quarterly sales? Monthly? Making our charts dynamic is more challenging than passing down new data. In fact, the chart will glitch and for half a second show the previous dataset — the dataset that isn’t even available in the current component. Which isn’t good.

You can look at my component in the repo if you want to add in the buttons and method I have for toggling between datasets. Otherwise you can write your own. There isn’t anything special or perfect about my buttons or methods. I’ll focus instead on the changes within myLineChart.js file.

qEryAvi.png!webjyMBRrJ.png!web
Dynamic Chart with Button Toggle

Our chart is currently set up in ComponentDidMount(). We did this so that we could be sure render() had occurred before searching for our chart ref. But now, even after a state change, our Chart won’t dynamically update. So let’s reconfigure:

EVJFFrM.jpg!web

What did we change?

  1. We separated our chart “building” functionality into its own function.
  2. We build the chart in ComponentDidMount() and componentDidUpdate().
  3. We assign our chart instantiation to a variable that we declare at the top of our file.
  4. We check if the chart was built previously. If so, we use the destroy() method before we create our chart.

You can use an alternative method that is listed in the docs for updating chart data if you wish to use update() instead.

Component vs Pure Component

zieM3ui.png!webfe226nN.png!web
Toggling irrelevant state forces the Chart to re-render

However, our component now re-renders even when irrelevant state changes in the parent component . In addition, the chart’s load animation will fire when no change to the chart has occurred. To fix this, we can change the myLineGraph.js to a PureComponent.

EVJFFrM.jpg!web

According to the React docs :

If your React component’s render() function renders the same result given the same props and state, you can use React.PureComponent for a performance boost in some cases.

However, they note that this is a shallow comparison. Meaning that for nested data structures, this might not perform the way we expect. If we were to have children within myLineGraph.js we would also want to make sure they were PureComponents. For our case, where the labels and datasets (reference values) change via setState(), we expect the same result given the same prop values, and we don’t have any children components — PureComponent is a good solution to prevent the re-render shown above.

Customizing Tooltips

Out of the box, the ChartJS tooltip is responsive and well-designed. For most use cases, the tooltip should be sufficient. However, a few lines of code can really help improve the styling or better match our branding.

Simple Customizations:

EVJFFrM.jpg!web

The tooltips object above represents the options that we can change. The properties are fairly straightforward, but you can read more in their official documentation . One property worth pointing: displayColors can be set to false if you want to remove the square color from the tooltip.

Changing the tooltip configuration should resolve the majority of your customization needs. In the case that you need a custom HTML tooltip, ChartJS provides us the code to get started:

EVJFFrM.jpg!web

Tooltip Positioning

In introducing a custom tooltip element, you will come across a common error: the tooltip will be cut off by the edge of the chart. By having access to this new HTML element, we have complete control to style and position the tooltip element. I’ve taken the liberty of writing this code to responsively position the element within the bounds of the chart.

EVJFFrM.jpg!web
Written for readability. You could customize it to be more concise if you wanted.

This code snippet can be added underneath the provided tooltip in the documentation:

var position = this._chart.canvas.getBoundingClientRect();

In addition, the tooltipEl from our above positioning code can be styled like any regular html element. You could add a class or add properties directly on to tooltipEl.style .

Customizing Data in the Tooltip

Without having to insert a custom HTML tooltip, we have a ton we can customize. Even the data we present within the tooltip. For instance, wouldn’t it be nice if we had a $ symbol in front of our dollar amounts for sales?

EVJFFrM.jpg!web
Callback function for customizing the tooltip label

In the above snippet, we return a template literal of the $ symbol with our given tooltip value. We’ve also removed the color block and increased font sizing.

QfeIRrf.png!webfQ7ZVbf.png!web
Tooltip with $ symbol added to value

Fun Challenge

In the mockData.js file, we have a branchManagerData array with data for 3 branches, including the total sum for each week’s performance.

  1. Import and pass down the branchManagerData, using the sum for your data plot points.
  2. Use a callback function for the tooltip label to display the remaining values in the branchManagerData array for the given week.

Return an array of values instead of a single value to get multiple lines like the example below:

return [Newark data, Austin data, Denver data];
QBrYNjY.png!web7FzQziy.png!web
Displaying branch data

Summary

Delving into ChartJS in a professional setting pushed me to improve design and customize the heck out of my set up. I had many pitfalls with HTML5 canvas, ChartJS, and React to get the data visualization results I wanted. Hopefully the problems (and solutions) in this article have helped you customize your chart to your liking.

Additional Resources:

If this article was helpful, be sure to leave :clap: or comments below. Feel free to follow me on Medium , or connect on LinkedIn .


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK