如何使用 React Native 制作数据可视化图表 - Victory Charts 教程
作者:Charles Mahler / 用例, 开发者, 产品
2022 年 2 月 24 日
导航至
一个美观的仪表板可以成就或毁掉您的应用程序。在本教程中,您将学习如何使用 React Native 和 Victory Native 图表库制作 iOS 和 Android 图表和数据可视化。
要求
要学习本教程,了解 JavaScript 的基本知识将有所帮助,最好熟悉 ReactJS 以及 NodeJS 开发环境。您还需要一个正在运行的 InfluxDB 实例,它将用于存储时间序列数据,您将创建图表以在本教程中可视化这些数据。最简单的方法是创建一个免费的InfluxDB Cloud 帐户。
为了在开发过程中预览您的 React Native 应用程序,您需要安装手机上的Expo Go 应用程序。您也可以使用Android Studio 模拟器或iOS XCode 模拟器,尽管这些模拟器有一些限制。
在浏览器中运行 Victory Native
Expo 还提供了将您的 React Native 应用程序部署到 Web 的功能。如果您想使用 Web 浏览器而不是智能手机或模拟器来预览您在本教程中制作的图表,您可以按照文档中的这些说明来配置您的 Expo 项目,以便 Victory Native 能够正确编译。
什么是 React Native?
React Native 是 Facebook 创建的一个框架,它允许 Web 开发者使用 ReactJS 和 JavaScript 生态系统创建原生应用程序,同时获得接近原生移动应用程序的性能和用户体验。结果是开发时间更快,因为大量的代码可以在 Android 和 iOS 应用程序之间共享,在某些情况下,Web 开发者可以在几乎不需要培训的情况下帮助开发移动应用程序。React Native 的另一个好处是,您不必将其用于整个移动应用程序——您可以选择对 iOS 和 Android 分别使用 Swift 或 Java,以用于应用程序中需要真正原生功能的地方。
在本教程中,您将使用 Expo,这是一种使 React Native 更易于使用的开发工具。安装过程将在本文后面介绍。
Victory Native 库简介
Victory Native 是一个可以在您的 React Native 应用程序中使用的图表库。它具有与 ReactJS Victory 库几乎相同的 API,后者可以用于 Web 应用程序。Victory Native 附带了许多预构建的图表组件、交互式元素和样式选项。Victory 在 GitHub 上拥有近 10,000 个星标和超过 150 位贡献者,已被 AirBnB、FiveThirtyEight、Zillow 和 Viacom 等公司使用。
Victory Chart 基础知识
Victory 提供了许多基本的图表组件,涵盖了许多最常见的用例。一个可以尝试使用的组件是 VictoryChart
组件,它充当子组件的包装器。这对于您想要将多种图表类型组合在一起的情况很有用,例如条形图和折线图。
上面图表的代码如下所示
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}
]
有用的 Victory Chart 属性
有许多通用的属性在图表组件中使用。以下是一些值得了解的属性:
data
-data
属性是实际显示任何内容所必需的。您可以将数据作为对象数组或嵌套数组传递。x
和y
-x
和y
属性用于指定图表的 x 轴和 y 轴值。如果数据格式为对象,则可以传递字符串作为值;如果您使用嵌套数组,则将传递您想要的索引的整数值。您还可以将函数传递给这些属性以根据输入计算值。-
style
- 此属性用于定义图表的样式,并使用 JS 样式中的 CSS。对于动态样式,许多值都可以传递函数来更改颜色或标签等内容。
要了解有关这些属性如何工作的更多信息,您可以查看 Victory 文档 – 它们为许多图表提供了实时预览功能,您可以在其中修改每个属性并查看效果。
Victory Charts 教程
要开始本教程,您首先需要使用以下命令安装 Expo CLI:
npm install --global expo-cli
现在导航到您想要放置项目的文件夹,并运行以下命令:
expo init my-app
导航到新创建的项目文件夹,并通过运行 expo start
启动您的 react native 应用程序。这应该会打开一个浏览器窗口,您可以在其中使用 Expo Go 应用程序扫描 QR 码,以在您的移动设备上查看该应用程序。
接下来,您将安装 InfluxDB 客户端库和 Victory Charts 库,以及 Victory 图表工作所需的 SVG 库:
expo install victory-native
expo install react-native-svg
npm install –save @influxdata/influxdb-client
一切都应该准备好使用 react native 了,现在您需要转到正在运行的 InfluxDB 实例并创建一个存储桶并将数据存储在其中。在本教程中,我们将使用 InfluxDB 提供的部分示例数据集。
空气传感器数据集将用于本教程。转到 InfluxDB 用户界面中的任务选项卡,并粘贴以下 Flux 查询,并将存储桶名称替换为您刚刚创建的存储桶名称:
import "influxdata/influxdb/sample"
sample.data(set: "airSensor")
|> to(bucket: "your-bucket-name" )
确保为任务命名并设置为每 15 分钟运行一次,因为这是示例数据更新的频率,然后单击保存。单击您创建的任务的齿轮图标,然后单击运行,以便它立即设置数据,而不是可能等待 15 分钟。
如果您转到 InfluxDB 中的数据浏览器并检查您的存储桶,您应该会看到一些可以可视化的已存储数据。在继续下一步之前,您需要创建 API 令牌,以便您可以从您的 React Native 应用程序查询数据。如果您在任何时候感到困惑或想了解更多关于您在用户界面中看到的内容,请查看文档。
从 React Native 查询 InfluxDB
在您的 Expo 项目的 App.js 文件中,您可以粘贴以下代码:
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"
}
});
此代码将向 InfluxDB 发出查询,以获取过去 30 分钟的空气传感器数据,转换返回的结果,将该数据放入 ReactJS 状态,然后将数据传递给 Victory 图表组件。结果将如下所示:
后续步骤
Victory 是一个可靠的图表库,其额外的好处是可以在 Web 上或使用 React Native 的移动应用程序中使用。如果您不打算长期使用此项目,请务必清理内容,关闭您之前创建的任务,使其不再持续运行。
本教程仅涵盖了使用 Victory 和 InfluxDB 的一些基本示例。如果您想了解更多信息,请查看以下资源。
- Nivo 教程 - 使用 Nivo 图表库可视化空气质量传感器数据
- Recharts 教程 - 使用 ReactJS 和 recharts 库可视化物联网传感器数据
- JS 客户端库入门教程 - 更深入地介绍如何使用 InfluxDB javascript 客户端库
- Flux 文档 - 用于使用 Flux 数据脚本语言的文档
- 使用 Flux 和 Postgres 丰富时间序列数据 - 教程涵盖如何使用 Flux 将来自 Postgres 的关系数据与来自 InfluxDB 的时间序列数据结合起来
- 5 个仪表板设计最佳实践 - 一些仪表板和数据可视化最佳实践,以帮助您制作更有效的图表
- InfluxDB 书籍 - 一本免费书籍,深入介绍了 InfluxDB 和周围的生态系统