Using ZingChart with React 17
ZingChart is a rich JavaScript data visualization library with support for over 50 chart types and modules. In this article we'll see how to get started with ZingChart and React and look at tapping into some ZingChart features from within a React app. This is not meant to be an exhaustive review of ZingChart's capabilities, to learn more about what ZingChart can do visit the ZingChart home page. The GitHub repository corresponding with this article is here and the ZingChart-React site documentation can be found here.
ZingChart is written in vanilla JavaScript and can be used in any web application, with or without a framework. We've written a wrapper to provide easy access to ZingChart from React. We'll start out with create-react-app and quickly get a static chart displayed. Then we'll look at each of the pages in the demo and highlight how to integrate ZingChart features like modules, events, and invoking methods on charts within a React context. We won't build the entire demo app in this article, so I recommend that you download the demo and start it up as a reference.
Hello World
First, though, let's create a Hello World ZingChart/React app. We'll start by creating a new React app, installing the zingchart-react
adapter, and starting the app. Note that installing the adapter also installs ZingChart itself.
npx create-react-app zingchart-react-helloworld
cd zingchart-react-helloworld
npm install zingchart-react
npm start
You can now view the app in a browser at localhost:3000
(check the output of npm start
, it might be running on a different port).
The full demo contains 7 pages demonstrating different aspects of using ZingChart in React. For this Hello World
example, we'll just add a static chart to the App.js
page.
Let's start by adding the imports for ZingChart and the adapter:
zingchart-react-helloworld/src/App.js
import 'zingchart/es6'
import ZingChart from 'zingchart-react'
Now remove the contents of the App()
function. You can also remove the import logo
statement at this time. Replace the contents of App()
with a return of a ZingChart component.
zingchart-react-helloworld/src/App.js
function App() {
return <ZingChart />
}
In the browser you should now see the ZingChart logo. Since we haven't configured the chart yet, it indicates that it's waiting for a configuration to be loaded. Above the return statement, define a config
object and add it as the data
attribute of the ZingChart component.
zingchart-react-helloworld/src/App.js
function App() {
const config = {
type: 'bar',
series: [{
values: [4,5,3,4,5,3,5,4,11]
}]
}
return <ZingChart data={config} />
}
You should now see a bar chart in the browser!
The config
object contains two properties. The first is type
, which selects which chart to display -- in this case, a bar chart. The second property is series
, which is an array of objects that contain the values and configuration for each dataset in the chart. In this case we're displaying one dataset and specifying its values
as an array of numbers. With no further configuration, the barchart axes are scaled to include the full range of the data and the bars are colored blue and have tooltips showing the numerical values.
This app can be used to play around with all of the different chart types and configuration parameters described in the ZingChart docs. Let's move on from here to look at various aspects of using ZingChart features in a React app.
zingchart-react-demo
For this part of the article you should have the demo running locally. I'll highlight key sections of the source code with a focus on the interaction between React and ZingChart.
In an environment with nodejs
, npm
, and git
installed:
git clone https://github.com/zingchart-demos/zingchart-react-demo.git
cd zingchart-react-demo
npm install
npm run start
You should now be able to view the demo in your browser at localhost:3000
. Each of the demo pages is contained in a file under zingchart-react-demo/src/components/
.
Hello World (zingchart-react-demo/src/components/Simple.js
)
The Hello World
demo is identical to the simple example we created above in zingchart-react-helloworld/src/App.js
. The only difference is that the component name has been changed from App
to Simple
.
US Map (zingchart-react-demo/src/components/ModuleChart.js
)
The primary purpose of this demo is to demonstrate how modules can be included for use in a ZingChart. ZingChart keeps file size to a minimum by implementing some functionality in optional modules.
In this case we're doing mapping, so we need the maps module and a module containing the specific map data we want to view. We're using the ES6 (ECMAScript 6) version of ZingChart, so we import the corresponding modules:
zingchart-react-demo/src/components/ModuleChart.js
import 'zingchart/es6'
import 'zingchart/modules-es6/zingchart-maps.min.js'
import 'zingchart/modules-es6/zingchart-maps-usa.min.js'
This page also shows representing ZingChart's configuration object as React state, rather than as a JavaScript constant. We're not modifying the state yet, but will in subsequent demos.
zingchart-react-demo/src/components/ModuleChart.js
const [config] = useState({
shapes: [
{
type: 'zingchart.maps',
options: {
name: 'usa',
ignore: ['AK', 'HI']
}
}
]
})
This configuration selects a chart of shapes
, which is an array of objects. Here we have one shape object of type zingchart.maps
consisting of the USA map minus Alaska and Hawaii. This is all we need to display a zoomable map of the lower 48 US states with tooltips.
Interaction (zingchart-react-demo/src/components/ModuleDrag.js
)
This page demonstrates using the ZingChart dragging module to interact with the data on a barchart. We also need to obtain a React reference to the chart component so we can invoke ZingChart methods.
First we import the dragging module. We'll also be using React's useState
and useRef
functions:
zingchart-react-demo/src/components/ModuleDrag.js
import React, {useState, useRef} from 'react'
...
import 'zingchart/modules-es6/zingchart-dragging.min.js'
Inside the component function, we create a React reference called chart
and assign it to the ZingChart component when we create it, using the ref
attribute:
zingchart-react-demo/src/components/ModuleDrag.js
const chart = useRef(null)
...
<ZingChart ref={chart} data={config} height='600px' modules='dragging' zingchart_plugins_dragging_complete={showData}/>
Note that, when we create the ZingChart component, we also enable the dragging
module. That module provides an attribute we can use to specify the function to call when the user has completed a dragging interaction with one of the bars in the chart: showData
. Here's that function's definition:
zingchart-react-demo/src/components/ModuleDrag.js
function showData() {
const data = chart.current.getseriesdata()
setGoodDays(countGoodDays(data[0].values, data[0].goals))
}
When we obtain a React reference to a ZingChart component, all of the methods provided by the ZingChart API are available, attached to the current
property of the reference. There's no need to specify the ID for the chart: chart.current.methodName(...)
is identical to zingchart.exec('chartID', 'methodName' ...)
. In this case, we're invoking chart.current.getseriesdata()
to obtain the series data from the chart. Let's look at the configuration for this chart:
zingchart-react-demo/src/components/ModuleDrag.js
const [config] = useState({
type: 'vbullet',
title: {
text: 'Pushups Per Day'
},
subtitle: {
text: 'Bars are draggable'
},
plot: {
valueBox: [
{
type: 'all',
text: '[%node-value / %node-goal-value]',
color: '#000',
placement: 'goal'
}
]
},
scaleX: {
labels: ['Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun']
},
tooltip: {
borderRadius: '3px',
borderWidth: '0px',
fontSize: '14px',
shadow: true
},
series: [
{
values: startingValues,
dataDragging: true,
goal: {
backgroundColor: '#64b5f6',
borderWidth: '0px'
},
goals: goals,
rules: [
{
backgroundColor: '#81c784',
rule: '%v >= %g'
},
{
backgroundColor: '#ef5350',
rule: '%v < %g/2'
},
{
backgroundColor: '#ffca28',
rule: '%v >= %g/2 && %v < %g'
}
]
}
]
})
There's a lot going on here, but note that, like in the Hello World
barchart, there is a series
property that's an array of objects, consisting of one object with a values
property containing an array of numbers. This object also enables dragging on just this dataset with the dataDragging
property. When we invoke chart.current.getseriesdata()
the current state of the series
property is returned and assigned to data
. data[0].values
contains the values corresponding to where the user has dragged each of the bars on the chart, and we use that to set the goodDays
React state, which is displayed above the barchart.
The configuration for this chart demonstrates how multiple elements can be shown in the same chart. In this case, the plot
property adds the non-draggable goal markers and text accompanying each bar in the chart. The configuration also sets the title, subtitle, tooltip options, and the labels for the X axis. The series
object not only sets the values for the bars, it also defines the goals
array that positions the goal markers. In addition it defines rules
that declaratively specify the color to use for each bar in terms of the relationship between the bar value %v
and the bar goal %g
.
Reconfiguring (zingchart-react-demo/src/components/Dynamic.js
)
Up to now we've stored the ZingChart configuration using React state, but we haven't modified it. This demo shows a dynamic line plot using React's useEffect
hook to start an interval timer to update the chart's state.
We construct the configuration for a line plot with some random data values and obtain both the state value and the function to modify it from React's useState
:
zingchart-react-demo/src/components/Dynamic.js
const [config, setConfig] = useState({
type: 'line',
series: [
{values: randomData(nValues)}
]
})
When we create the ZingChart component we just use config
as we have before, as the data
property. We also display config.series[0].values
in a div
above the chart. Both the chart and the div will be re-rendered each time we call setConfig
:
zingchart-react-demo/src/components/Dynamic.js
return <div>
<div>{JSON.stringify(config.series[0].values)}</div>
<ZingChart data={config} />
</div>
We use React's useEffect
hook to start the interval timer that invokes shuffle()
once a second, after the component has been mounted. The function passed as the first argument to useEffect
returns a function that will be invoked to clear the interval timer when this component is unmounted. We also use the second, optional argument to useEffect
, which lists the variables that should be monitored for changes to trigger this effect. By providing an empty list here, we specify that we only want this effect to trigger once. Without this second argument, the effect would trigger every time the component renders, and we'd end up with a lot of interval timers!
zingchart-react-demo/src/components/Dynamic.js
useEffect(() => {
const interval = setInterval(shuffle, period)
// Invoked to clean up when unmounted
return () => clearInterval(interval)
}, [])
The shuffle
function calls setConfig
with a function that takes the current state of config
and modifies the series
array to contain an object with a new, random values
array:
zingchart-react-demo/src/components/Dynamic.js
function shuffle() {
setConfig(state => ({...state, series: [{values: randomData(nValues)}]}))
}
Events (zingchart-react-demo/src/components/Events.js
)
This demo shows how to listen for events occuring on a ZingChart. We're going draw a line plot and watch for an event indicating the chart has finished rendering and for mouse-over events on the nodes of the line plot.
We start by declaring the configuration for a simple line plot and two React states:
zingchart-react-demo/src/components/Events.js
const [config] = useState({
type: 'line',
series: [{
values: [4,5,3,4,5,3,5,4,11]
}]
})
const [output, setOutput] = useState('')
const [renderState, setRenderState] = useState('pending')
The JSX returned by this component looks like this:
zingchart-react-demo/src/components/Events.js
return (
<div className="Events-wrapper">
<ZingChart data={config} complete={chartDone} node_mouseover={nodeInfo} />
<div className="Events-output">
<h2>Output from events</h2>
<div className="Events-bound">
Events bound:
<ul>{events}</ul>
</div>
<div className="Events-renderState">Chart is {renderState}</div>
<pre className="Events-nodeInfo">{output}</pre>
</div>
</div>
)
The ZingChart component specifies the functions to call when the complete
and node_mouseover
events occur. The rest of the component displays the corresponding renderState
and output
React states.
The event functions themselves are straight-forward. chartDone
just sets renderState
to rendered
:
zingchart-react-demo/src/components/Events.js
function chartDone() {
setRenderState('rendered')
}
Since there's no perceptible delay in rendering the chart, we never get to see pending
being displayed.
nodeInfo
receives an argument containing information about the mouse-over event. The ev
property of this object contains the JavaScript event data, which includes some circular references that make it impossible to render as JSON (plus it's not interesting), so we remove info.ev
before rendering info
as JSON and storing it in output
:
zingchart-react-demo/src/components/Events.js
function nodeInfo(info) {
delete info.ev // Remove the event data
setOutput(`Node Info\n${JSON.stringify(info,null,2)}\n`)
}
Some of the properties of info
pertain to the chart itself and don't change, but as you mouse over different nodes you can see changing values like the index and value of the node, and its (x,y) coordinates in the rendered chart image.
Methods (zingchart-react-demo/src/components/Methods.js
)
This demo is another example of using a React reference to the ZingChart component in order to invoke methods on it. In this case we're using the addplot
method to add an additional random dataset to a bar chart:
zingchart-react-demo/src/components/Methods.js
function addDataset() {
chart.current.addplot({
data: {
values: randomData(nValues),
}
})
setNSets(n => n + 1)
}
Multiple Plots (zingchart-react-demo/src/components/License.js
)
The purpose of this final demo is to show how to access the zingchart
object in order to access properties and methods that operate on ZingChart globally (see ZingChart Object and Methods).
Importing zingchart/es6
creates the zingchart
global variable, but to make that more explicit we can modify the import statement:
zingchart-react-demo/src/components/License.js
import zingchart from 'zingchart/es6'
We can then use zingchart
to set some performance flags:
zingchart-react-demo/src/components/License.js
zingchart.LICENSE = ['abcdefghijklmnopqrstuvwxy']
This demo also shows how to display multiple plots in one ZingChart instance. The configuration object used here:
zingchart-react-demo/src/components/License.js
const [config] = useState({
graphset: [
{
type: 'line',
height: '200px',
width: '90%',
x: '5%',
y: '5%',
series:[
{
values:[76,23,15,85,13]
},
{
values:[36,53,65,25,45]
}
]
},
{
type: 'funnel',
height: '55%',
width: '45%',
x: '5%',
y: '200px',
series:[
{values:[30]},
{values:[15]},
{values:[5]},
{values:[3]}
]
},
{
type: 'pie',
height: '55%',
width: '45%',
x: '50%',
y: '200px',
series:[
{values:[15]},
{values:[30]},
{values:[34]}
]
}
]
})
consists of a graphset
array containing three objects, each of which is a ZingChart configuration of the form that we've seen in each of the previous demos. In addition to specifying the type and data for each plot, the size and position of the plot is set using the height
, width
, x
, and y
properties. For more on creating sets of graphs in one ZingChart, see the graphset documentation.
Conclusion
We hope this article has provided a jumpstart in using ZingChart in a React application. For more information, please visit the GitHub repository for the zingchart-react
integration module https://github.com/zingchart/zingchart-react and the ZingChart-React documentation at https://www.zingchart.com/docs/integrations/react.
Interested in learning more about ZingChart or trying it out for yourself? Let us know at support@zingchart.com or visit us at https://www.zingchart.com and chat directly with our team!