How to Make Data Visualizations with React Native - Victory Charts Tutorial

Navigate to:

A nice dashboard can make or break your app. In this tutorial, you will learn how to make iOS and Android charts and data visualizations using React Native and the Victory Native charting library.

Requirements

To follow this tutorial, it will help to have a basic understanding of JavaScript and ideally some familiarity with ReactJS to go with a NodeJS development environment. You will also need a running instance of InfluxDB which will be used to store the time series data that you will be creating charts to visualize in this tutorial. The easiest way to do this is to create a free InfluxDB Cloud account.

For previewing your React Native app during development, you will want to install the Expo Go app on your phone. You could also use the Android Studio emulator or iOS XCode simulator, although there are some limitations with these.

Running Victory Native in the browser

Expo also provides the ability to deploy your React Native app to the web. If you want to preview the charts you make in this tutorial using your web browser rather than using your smartphone or an emulator, you can follow these instructions in the docs to configure your expo project so Victory Native compiles properly.

What is React Native?

React Native is a framework created by Facebook that allows web developers to create native applications using ReactJS and the JavaScript ecosystem while getting the performance and user experience close to a native mobile application. The result is faster development time because a large amount of code can be shared across Android and iOS apps, and in some cases, web developers can help develop mobile applications with minimal training required. An additional benefit of React Native is that you don’t have to use it for the entire mobile app – you have the option to use Swift or Java for iOS and Android respectively for places in the app where you need true native functionality.

For this tutorial you will be using Expo, a development tool to make using React Native even easier. The installation process will be covered later in this article.

Intro to Victory Native library

Victory Native is a charting library that can be used in your React Native applications. It has an almost identical API to the ReactJS Victory library that can be used for web applications. Victory Native comes with a number of pre-built chart components, interactive elements, and styling options. Victory has nearly 10,000 stars on GitHub with over 150 contributors and has been used by companies like AirBnB, FiveThirtyEight, Zillow, and Viacom.

Victory Chart basics

Victory provides a number of basic chart components that cover many of the most common use cases. One component to play around with is the VictoryChart component which acts as a wrapper around children components. This is useful for situations where you want to combine multiple chart types together, for example a bar chart and line chart.

Victory-Native

The code for the above chart looks like this:

VictoryChart domainPadding={{ x: 8 }}>
  <VictoryBar
    style={{
      data: { fill: "#c43a31" }
    }}
    data={sampleData}
  />
  <VictoryLine
    data={sampleData}
  />
</VictoryChart>
```

The sample data:
```Javascript
const sampleData = [
  {x:1, y:4},
  {x:2, y:7},
  {x:3, y:3},
  {x:4, y:4},
  {x:5, y:6}
]

Useful Victory Chart props

There are a number of common props used across chart components. The following are a few that are worth knowing:

  • data - The data prop is required to actually display anything. You can pass your data as an array of objects or a nested array.
  • x and y - The x and y props are used to specify the x-axis and y-axis values for your charts. If the data is formatted as an object you can pass a string as the value; if you use a nested array you will pass an integer value for the index you want. You can also pass a function to these props to calculate a value based on the input.
  •  style - This prop is used to define the styling of your chart and uses a CSS in JS style. For dynamic styling many of the values can be passed functions to change things like color or labels.

To learn more about how these props work, you can check out the Victory docs – they have a live preview feature for many of the charts where you can modify each prop and see the effect.

Victory Charts tutorial

To get started on the tutorial you’ll first need to install the Expo CLI with the following command:

npm install --global expo-cli

Now navigate to whatever folder you want your project to be located in and run the following command:

expo init my-app

Navigate to the newly created project folder and start your react native app by running expo start. This should open a browser window where you can scan the QR code with the Expo Go app to see the app on your mobile device.

Next you will install the InfluxDB Client library and Victory Charts libraries, along with an SVG library required for Victory charts to work:

expo install victory-native expo install react-native-svg npm install –save @influxdata/influxdb-client

Everything should be ready to go with react native, now you need to go to your running InfluxDB instance and create a bucket and store data in it. For this tutorial we’ll use some of the sample datasets available with InfluxDB.

The air sensor dataset will be used for this tutorial. Go to the tasks tab in the InfluxDB user interface and paste the following Flux query with the bucket name switched for the bucket name you just created:

import "influxdata/influxdb/sample"

sample.data(set: "airSensor")
  |> to(bucket: "your-bucket-name"  )

tasks tab

Make sure to give the task a name and set it to run every 15 minutes, as that’s how often the sample data is updated and then click Save. Click on the wheel icon for the task you created, and then click Run so it will set the data now, instead of potentially waiting 15 minutes.

create-task

If you go to the Data Explorer in InfluxDB and check your bucket, you should see some data stored that you can visualize. Before moving to the next step, you will need to create an API token so you can query the data from your React Native app. If you get confused at any point or want to learn more about something you see in the user interface, check out the docs.

Querying InfluxDB from React Native

In the App.js file of your expo project you can paste the following code:

import React, {useState, useEffect} from "react";
import { InfluxDB } from "@influxdata/influxdb-client";
import '@expo/browser-polyfill';
import { StyleSheet, View, Text } from "react-native";
import { VictoryBar, VictoryChart, VictoryLine } from "victory-native";

//from https://github.com/facebook/react-native/issues/21209
FileReader.prototype.readAsArrayBuffer = function (blob) {
	if (this.readyState === this.LOADING) throw new Error("InvalidStateError");
	this._setReadyState(this.LOADING);
	this._result = null;
	this._error = null;
	const fr = new FileReader();
	fr.onloadend = () => {
		const content = atob(fr.result.substr("data:application/octet-stream;base64,".length));
		const buffer = new ArrayBuffer(content.length);
		const view = new Uint8Array(buffer);
		view.set(Array.from(content).map(c => c.charCodeAt(0)));
		this._result = buffer;
		this._setReadyState(this.DONE);
	};
	fr.readAsDataURL(blob);
}
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const atob = (input = '') => {
	let str = input.replace(/=+$/, '');
	let output = '';

	if (str.length % 4 == 1) {
		throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
	}
	for (let bc = 0, bs = 0, buffer, i = 0;
		buffer = str.charAt(i++);

		~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
			bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
	) {
		buffer = chars.indexOf(buffer);
	}

	return output;
}

const token = 'your-api-token'
const org = 'org-id'
const url = 'cloud-url'
const bucket = 'bucket-name'

let query = `from(bucket: "bucket-name")
  |> range(start: -30m)
  |> filter(fn: (r) => r["_measurement"] == "airSensors")
  |> filter(fn: (r) => r["_field"] == "humidity" or r["_field"] == "temperature" )
  |> filter(fn: (r) => r["sensor_id"] == "TLM0102")
  |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
  |> yield(name: "results")`
const InfluxChart = () => {
  const [data, setData] = useState([])

  useEffect(() => {

    const influxQuery = async () => {
      //create InfluxDB client
      const queryApi =  new InfluxDB({ url, token }).getQueryApi(org);
      //make query
       queryApi.queryRows(query, {
        next(row, tableMeta) {
          const o = tableMeta.toObject(row);
          //push rows from query into an array object
          res.push(o);
        },
        complete() {
          let finalData = []

          for (let i = 0; i < res.length; i++) {
            let point = {};
            point["humidity"] = res[i]["humidity"];
            point["temperature"] = res[i]["temperature"];

            point["name"] = res[i]["_time"];
            finalData.push(point);
          }
          setData(finalData); 
        },
        error(error) {

          console.log("query failed- ", error);
        }
      });
    };

    influxQuery();
  }, []);

  return(
    <View>
      <VictoryChart height={600} domainPadding={20}>
        <VictoryLine style={{data: {stroke: '#34d5eb', strokeWidth: 1}}} data={data} x="name" y='humidity' />
        <VictoryLine style={{data: {stroke: '#a534eb', strokeWidth: 1}}} data={data} x="name" y="temperature"/>
      </VictoryChart>
    </View>
  )
}

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Victory Native</Text>
        <InfluxChart />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#f5fcff"
  }
});

This code will make a query to InfluxDB to grab the air sensor data from the last 30 minutes, transform the returned results, put that data into ReactJS state, and then pass the data to the Victory chart component. The result will look like this:

Querying-InfluxDB-from-React-Native

Next steps

Victory is a solid charting library with the added bonus of being able to be used on the web or in mobile apps using React Native. If you don’t plan on using this project long term be sure to clean things up by turning off the task you created earlier so it isn’t running constantly.

This tutorial just covered some basic examples for using both Victory as well as InfluxDB. If you want to learn more, check out some of the resources below.