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!