Recharts 和 InfluxDB 教程 - 使用 ReactJS 可视化 IoT 传感器数据
作者:Charles Mahler / 产品, 用例, 开发者
2022 年 1 月 7 日
导航至
在本教程中,您将学习如何使用 Recharts 图表库通过 ReactJS 创建自定义数据可视化,以显示使用 InfluxDB 存储的时间序列数据。为此,您将存储一些由 IoT 传感器记录的实时数据,这些传感器记录房间的温度、湿度和一氧化碳水平。
在本篇文章结束时,您将对使用 Recharts 的基础知识有扎实的理解,并初步了解如何使用 InfluxDB 写入和读取时间序列数据。
什么是 Recharts?
Recharts 是一个流行的图表和数据可视化库,与 ReactJS 一起使用。Recharts 最初于 2016 年发布,是最早专门为 ReactJS 应用程序设计的图表库之一,该库本身是使用 React 和底层的 D3js 构建的。
像大多数优秀的开源项目一样,Recharts 的设计是在构建业务应用程序时面临实际问题和痛点之后进行的。当时的主要问题是尝试将已建立的 JavaScript 图表库集成到 ReactJS 应用程序中,事实证明这很笨拙。通过在 React 本身中创建一个图表库,开发人员将能够利用 React 的虚拟 DOM 使图表更快,使用 React 的原生组件生命周期系统,并使自定义图表更容易。Recharts 提供的一些最佳功能是
- 11 种不同类型的图表
- 易于理解的 API
- 解耦的组件架构
- 灵活且易于自定义
- 强大的社区使其易于解决任何问题
Recharts 的主要目标是使用普通的 React 组件在 ReactJS 应用程序中简单地创建图表,提供原生 SVG 支持,并为组件提供声明式 API。可以肯定地说,Recharts 在这些目标上取得了成功,因此现在在 GitHub 上拥有超过 17,000 颗星,数百名贡献者,以及许多公司在生产中使用它。所有这些都使 Recharts 成为在您自己的应用程序中使用的安全选择。
什么是 InfluxDB?
InfluxDB 是一个 NoSQL 时间序列数据库,这意味着 InfluxDB 针对存储、查询和 分析时间序列数据 进行了优化。时间序列数据 是按时间顺序索引的数据点序列。时间序列数据的一些常见示例是
除了能够有效地存储和查询时间序列数据外,InfluxDB 还可以作为一个用于处理数据的工具平台。这包括基于您的数据创建无服务器任务和警报的功能、使用 Flux 查询语言进行常见时间序列数据分析的预构建函数,以及用于显示数据的易于使用的仪表板。
Recharts 入门
在开始本教程之前,我将简要介绍 Recharts 库,并介绍一些高级概念,这些概念对于在超出本特定教程中涵盖的用例之外使用 Recharts 将非常有用。
图表数据格式
为了使您的图表正确显示,您需要将数据作为 prop 提供给图表。数据结构通常是对象数组,每个对象代表一个数据点。这是一个条形图的示例数据值
const data = [
{
name: 'Page A',
uv: 4000,
pv: 2400,
amt: 2400,
},
{
name: 'Page B',
uv: 3000,
pv: 1398,
amt: 2210,
},
{
name: 'Page C',
uv: 2000,
pv: 9800,
amt: 2290,
},
{
name: 'Page D',
uv: 2780,
pv: 3908,
amt: 2000,
},
{
name: 'Page E',
uv: 1890,
pv: 4800,
amt: 2181,
},
{
name: 'Page F',
uv: 2390,
pv: 3800,
amt: 2500,
},
{
name: 'Page G',
uv: 3490,
pv: 4300,
amt: 2100,
},
];
您将使用代码中的属性将这些对象键映射到 Recharts
图表 props
每种图表类型都有一些可以传递以自定义它的独特 props,但也有一些所有图表通用的属性值得了解
- 数据: 整个 `data` 对象需要传递给父图表组件
- DataKey: `dataKey` prop
- 高度/宽度: 高度和宽度将以固定像素数确定图表的大小。如果您想要一个响应式图表,Recharts 还提供了一个响应式容器组件,该组件将包装您的图表组件并占用可用空间的百分比
- Stroke/Fill: 这些 props 用于定义图表的线条/条形的颜色。
Recharts 图表组件的结构
Recharts 的设计理念是万物皆组件。因此,您经常会嵌套组件以获得您想要的图表。例如,工具提示、网格和图表轴都是通过将提供的组件嵌套到图表父元素中添加到图表中的。
Recharts 提供的最高级别组件是 ResponsiveContainer
组件,该组件可用于包装图表,以便它们可以具有响应式高度和宽度。
Recharts 库中一些最常用的实用程序组件是
- Tooltip - 可用于在用户将鼠标悬停在数据点上时显示数据点的确切值。格式完全可自定义
- Legend - 用于显示图表单位名称/值的组件
- YAxis/XAxis - 用于向图表添加轴的组件
使用 Recharts 的基本折线图
理论知识就够了:是时候看看一些真实的代码,用 Recharts 实现一个简单的折线图了
import {
LineChart,
Line,
Tooltip,
CartesianGrid,
YAxis,
Legend
} from "recharts";
const data = [
{
name: "requests",
count: 1500
},
{
name: "requests",
count: 1720
},
{
name: "requests",
count: 1650
},
{
name: "requests",
count: 1510
},
{
name: "requests",
count: 1400
}
];
export const BasicLine = () => {
return (
<LineChart width={400} height={400} data={data}>
<Line type="monotone" dataKey="count" />
<CartesianGrid />
<YAxis />
<Legend verticalAlign="top" height={30} />
<Tooltip />
</LineChart>
);
};
我将此图表放在新创建的 components
文件夹中,然后将其导入到主 App.js
文件中,以便由 React 渲染。这非常简陋,但为您提供了一个如何使用 Recharts 的基本示例。需要注意的关键是 dataKey
prop,它用于定义折线图的值。如果一个对象有多个键,您仍然可以控制哪个键用于图表值。在继续主要项目之前,请务必查看文档并试用折线图 props,以便更好地了解 Recharts。
设置 InfluxDB
本教程将涉及在 InfluxDB 中 存储时间序列数据,然后查询该数据以使用 Recharts 可视化它。您可以在几分钟内创建一个免费层 InfluxDB Cloud 帐户,或者下载并安装 开源版本。
一旦您运行了 InfluxDB 实例,您需要按照以下步骤进行设置,以便使用 JavaScript 客户端库使用 InfluxDB API
- 创建组织 - 这将在创建您的帐户时完成,如果您想更改或忘记组织名称,可以在您的用户设置中找到它。
- 创建 bucket 以存储数据 - bucket 名称将在我们的代码中用于查询数据。
- 为您的 bucket 创建 API 令牌 - 创建 bucket 后,您需要创建一个 API 令牌,该令牌将用于验证从客户端库发出的 API 请求。
安装项目要求
要遵循本教程,您将需要一个 NodeJS 开发环境设置。Create React App 将用作项目的起始模板,但您可以使用您感觉最舒适的任何设置。如果您不想在本地计算机上设置任何内容,您可以使用像 Codesandbox 或 Replit 这样的工具在浏览器中创建开发环境。这两项服务都是免费的,并提供 React 模板,因此您可以立即开始。
如果您在本地开发并使用 VS Code 作为编辑器,您可以试用 Flux VS Code 扩展,它将为您提供语法高亮、自动完成以及与 VS Code 内 InfluxDB 的直接集成。这将使测试和运行您的 InfluxDB 查询更容易,所以请查看一下。
本教程需要 InfluxDB JavaScript 客户端库和 Recharts,可以使用以下命令安装
npm install @influxdata/influxdb-client recharts
您还需要获取您之前创建的 API 令牌,并将其存储为环境变量或代码中的变量。请注意,这是一个安全问题,不应在生产中使用,因为您的 API 令牌将嵌入到浏览器中的公共前端源代码中。
在生产环境中,您希望在服务器端处理这些 API 调用,以便您的令牌是安全的。您可以在我们的 Highcharts 教程 中看到如何完成此操作的示例。另一种选择是在客户端为您 API 令牌提供只读访问权限并设置短寿命,以便它快速过期。
将数据写入 InfluxDB
接下来,您需要将一些数据写入 InfluxDB,以便您可以在本教程的后面部分查询和可视化数据。InfluxDB 提供了多种写入数据的方式
- Telegraf - Telegraf 是 InfluxData 创建的一个开源服务器代理,用于收集、处理数据,然后将数据输出到存储,而无需任何代码。Telegraf 有超过 250 个数据源的输入插件,可以将数据输出到 40 个不同的数据存储。如果您有兴趣学习如何使用 Telegraf,可以按照此 入门教程 进行操作。
- 文件上传 - InfluxDB UI 提供了 上传 CSV 或文本文件 的功能,这些文件以 Line Protocol 格式化。如果您想快速测试一些数据,也可以直接在 UI 中键入数据并提交几个数据点。
- 客户端库和 REST API - InfluxDB 通过其 REST API 直接访问所有功能,从而为开发人员在如何存储和查询数据方面提供最大的灵活性。客户端库 也适用于 13 种不同的编程语言。这些库充当 REST API 的包装器,并为开发人员提供了许多功能,使使用 InfluxDB 开发应用程序更容易。
- 示例数据 - InfluxDB 包含一些 示例数据集,可以使用 Flux 写入 bucket。
在本教程中,我们将使用示例数据集来降低复杂性。有几个不同的时间序列数据集可用,但对于本教程,我们将使用空气传感器数据,该数据提供建筑物中多个房间的温度、湿度和一氧化碳传感器数据。
此数据每 15 分钟更新一次,因此为了确保您的数据保持最新,您需要创建一个 InfluxDB 任务,该任务将每 15 分钟更新您 bucket 中的数据。单击侧边栏上的“任务”图标,然后单击“创建任务”。
为您的任务命名,然后将“每”列值设置为“15m”,以便任务每 15 分钟运行一次。将以下 Flux 代码粘贴到编辑器中
import "influxdata/influxdb/sample"
sample.data(set: "airSensor")
|> to(bucket: "your-bucket-name" )
确保将 bucket 名称更改为您之前创建的 bucket。
单击保存并返回到任务仪表板页面。单击您创建的任务的齿轮图标,然后单击运行,以便立即运行,这样您就不必等待 15 分钟。
现在返回到数据浏览器并检查您的 bucket。如果您按照所有步骤操作,当您提交查询时,您应该会看到您的数据。如果您没有看到任何数据,请尝试刷新浏览器并仔细检查您是否正在查看正确的 bucket。如果您仍然没有看到任何数据,请尝试查看文档并确保您按照了所有之前的步骤,并且没有遗漏任何内容。
从 InfluxDB 查询数据
现在我们已经存储了一些数据,我们可以开始进行查询了。在本教程中,您将使用 Flux,这是一种从头开始为处理时间序列数据而设计的查询语言。Flux 提供了许多内置函数来处理时间序列数据,从而简化了您作为开发人员的工作。它在语法方面与 JavaScript 有些相似之处,因此很快就能掌握基础知识。
开始查询存储在 InfluxDB 中的数据的最简单方法是使用用户界面中的 数据浏览器。您可以直观地修改查询,并实时查看数据如何变化。然后,您可以从可视化查询构建器切换过来,查看实际的底层 Flux 查询是什么样的。试用数据,然后查看查询可能是学习 Flux 的最快方法。有关 Flux 可以做什么的完整介绍和概述,请务必阅读 文档。
在高层次上,Flux 查询有 4 个主要部分
- 源 - 正在查询的特定 bucket
- 过滤器 - 在大多数情况下,您只需要 bucket 中部分数据,因此您将根据时间范围进行过滤,并且可能选择您需要的每个数据点中的几个字段。
- 形状 - 在此阶段,您可能希望在进一步处理之前修改数据格式。这可以包括将列透视为行、删除列或按键对数据进行分组等操作。
- 处理 - 最后阶段是处理数据。这方面的一些示例包括聚合剩余值、选择特定的最小值或最大值、重写或修改每行值,或者分析数据,然后根据结果发送警报。
这是一个遵循上述结构的简单 Flux 查询
from(bucket: "example-bucket") // ?? Source
|> range(start: -1d) // ?? Filter on time
|> filter(fn: (r) => r._field == "foo") // ?? Filter on column values
|> group(columns: ["sensorID"]) // ?? Shape
|> mean() // ?? Process
使用 Recharts 可视化数据
完成所有设置后,我们终于可以开始处理好的部分了。在本节中,我们将利用 Recharts ComposedChart
组件,该组件允许我们将多种不同类型的图表组合在一起。结果将是一个折线图和面积图,向我们显示 IoT 传感器正在跟踪的特定房间的湿度和温度。
您要做的第一件事是在您之前创建的 components
文件夹中创建一个新组件。您可以随意命名它,但我将命名我的组件为 InfluxChart
,这将是它导入到 App.js
文件中的方式。
这是图表组件的代码。我将在下面逐步讲解
import React, { useState, useEffect } from "react";
import { InfluxDB } from "@influxdata/influxdb-client";
import {
ComposedChart,
Bar,
Line,
XAxis,
YAxis,
Tooltip,
Legend,
CartesianGrid,
Area
} from "recharts";
const token = "your-API-token";
const org = "org-name";
const url = "INFLUX_DB_URL";
let query = `from(bucket: "air-sensor")
|> 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")
`;
export const InfuxChart = () => {
const [data, setData] = useState([]);
useEffect(() => {
let res = [];
const influxQuery = async () => {
//create InfluxDB client
const queryApi = await new InfluxDB({ url, token }).getQueryApi(org);
//make query
await 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 (
<div>
<h1>Influx Chart</h1>
<ComposedChart width={900} height={400} data={data}>
<CartesianGrid />
<Tooltip />
<Line
stroke="#0ff770"
strokeWidth={1}
dataKey="temperature"
dot={false}
/>
<Area stroke="#bf04b3" fill="#f235e6" dataKey="humidity" />
<XAxis hide dataKey="name" />
<YAxis />
</ComposedChart>
</div>
);
};
这是生成的图表:
以下是上面代码中发生的事情
- Flux 查询 - Flux 查询首先使用
range
函数获取过去 30 分钟存储在我们 bucket 中的所有传感器数据。然后过滤该数据以仅获取airSensors
measurement,再次过滤以仅获取humidity
和temperature
字段,然后最后一次过滤以仅获取 ID 为TLM0102
的传感器的数据。最后,使用pivot
函数根据时间戳组合湿度和温度字段值,以便它们成为单行值。 - useEffect - 在图表组件中,最复杂的代码是 useEffect 钩子,它使用客户端库对 InfluxDB 进行 API 调用。查询结果会被追加到
res
结果数组中。一旦调用返回,complete
内部的代码就会运行。在这里,我们循环遍历结果数组并修改结果,然后再将它们添加到finalData
数组中。然后,我们使用 React useState 钩子提供的setData
来将数据设置到组件的状态中,以便图表可以显示数据。 - 图表组件 - 图表本身的 JSX 非常简单,数据被传递到 data 属性中,
Line
和Bar
组件各自取一个值来显示。
App.js 文件很简单,我们只是导入了放置在 components 文件夹中的图表组件
import { InfluxChart } from "./components/InfluxChart";
export default function App() {
return (
<div className="App">
<InfluxChart />
</div>
);
}
所以你已经设置了一些基本的图表,但这有点无聊,下一步是什么?好消息是,这个项目可以向许多不同的方向扩展。你可以更深入地研究你正在使用 Recharts 或 InfluxDB 做什么。这里有一些想法:
- 自定义图表事件处理程序 - Recharts 允许你将自定义函数传递给图表中的每个组件,以处理各种事件,例如鼠标悬停、点击以及鼠标离开或进入组件区域。这为你提供了许多自定义图表行为的选项。
- 连接不同的数据源 - 示例数据并不是最令人兴奋的数据,好消息是有大量不同的选项可用。你可以查看 Telegraf 的 250 多个集成,并找到一个看起来很有趣的数据源来尝试。
- 动态图表 - 因为 Recharts 就像任何其他 React 组件一样,你可以在主图表组件内组合线条或柱状图等元素。以我们在本教程中使用的传感器数据为例,你可以设置你的图表,以便在添加更多房间传感器时它们仍然可以工作,而不是仅限于静态值。
- 设置警报或自动化任务 - 你已经创建了一个任务来确保空气传感器数据每 15 分钟更新一次,但是你可以使用 InfluxDB 任务 做更多的事情。你可以设置阈值来监控你的数据,然后在违反这些阈值时创建自动化操作。
其他资源
既然你已经掌握了使用 InfluxDB 和 Recharts 的良好基础,我将为你提供一些资源,如果你想将你的知识提升到一个新的水平,可以查看这些资源。
- InfluxDB 书籍 - 这本书详细介绍了 InfluxDB 的所有内容。它详细介绍了如何使用 Flux 以及如何优化你的查询。它还涵盖了 InfluxDB 的数据模型和周围的工具生态系统,以便你可以充分利用 InfluxDB。
- InfluxDB JavaScript 客户端库入门- 本教程主要侧重于 Recharts,并且在某种程度上略过了客户端库本身的一些细节,如果你想要一些额外的信息,可以阅读这篇博文。
- Recharts 文档
- InfluxDB IoT 中心 - 一个更复杂的示例 IoT 应用程序,由 InfluxDB 提供支持
- InfluxDB 文档
- 什么是时间序列数据库?