You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Highcharts、Firebase、ReactJS+TypeScript实现实时图表?

如何连接Firebase实现React+TypeScript+Highcharts实时图表获取新增数据

步骤1:配置Firebase依赖与初始化

  • 安装Firebase SDK:npm install firebase 或 yarn add firebase
  • 创建Firebase配置文件(比如firebaseConfig.ts),填入你的项目信息:
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);

步骤2:重构实时心率图表组件

移除原有的随机数据生成逻辑,改用Firestore的实时监听获取并更新图表数据:

修改后的GraphHeartRateMonitor组件:

import { useEffect, useRef } from 'react';
import { TooltipFormatterContextObject } from 'highcharts';
import { Chart, labelsStyle } from '../../../../shared/components/DataDisplay/Chart';
import { defaultTheme } from '../../../../shared/styles/themes';
import { formatDate } from '../../../../shared/utils/date';
import { db } from '../../../../firebaseConfig'; // 引入你的Firebase配置
import { collection, query, orderBy, limit, onSnapshot } from "firebase/firestore";

export const GraphHeartRateMonitor: React.FC = () => {
  const chartRef = useRef<HighchartsReact.RefObject>(null);
  const unsubscribeRef = useRef(() => {});

  // 初始化图表配置
  const options: Highcharts.Options = {
    chart: {
      borderWidth: 0,
      height: '176px',
      plotBackgroundColor: defaultTheme.palette.background.default,
      type: 'line',
      marginRight: 10,
    },
    title: { text: '' },
    xAxis: {
      type: 'datetime',
      tickPixelInterval: 300,
      labels: { enabled: false },
    },
    yAxis: {
      title: { text: '' },
      gridLineColor: defaultTheme.palette.background.default,
      tickInterval: 10,
      labels: { enabled: false },
      plotLines: [
        {
          value: 120,
          color: defaultTheme.palette.red[300],
          width: 2,
          label: {
            text: '120',
            useHTML: true,
            verticalAlign: 'middle',
            align: 'left',
            style: {
              background: defaultTheme.palette.red[300],
              borderRadius: '24px',
              height: '18px',
              padding: '2px 12px',
              color: defaultTheme.palette.common.white,
              fontFamily: 'Roboto Mono',
              fontSize: '12px',
              fontWeight: '400',
            },
          },
        },
      ],
    },
    legend: { enabled: false },
    exporting: { enabled: false },
    series: [
      {
        name: '心率数据',
        color: defaultTheme.palette.grey[900],
        data: [], // 初始空数据,后续从Firebase加载
      },
    ],
    tooltip: {
      useHTML: true,
      backgroundColor: defaultTheme.palette.background.paper,
      borderRadius: 4,
      shadow: { color: defaultTheme.palette.common.black },
      padding: 12,
      style: labelsStyle(20, defaultTheme.palette.grey[300]),
      formatter() {
        const self: TooltipFormatterContextObject = this;
        return `
          <span>时间:</span>
          <strong>${formatDate(new Date(self.x), 'HH:mm:ss')}</strong>
          </br>
          <span>心率:</span>
          <strong>${self.y} bpm</strong>
        `;
      },
    },
  };

  useEffect(() => {
    // 构建Firestore查询:按时间排序,取最近20条数据(和原模拟数据数量一致)
    const heartRateCollection = collection(db, 'heartRates');
    const q = query(heartRateCollection, orderBy('timestamp', 'desc'), limit(20));

    // 实时监听数据变化
    unsubscribeRef.current = onSnapshot(q, (snapshot) => {
      const chart = chartRef.current?.chart;
      if (!chart) return;

      const newData: [number, number][] = [];
      // 反转数据,保证时间从旧到新
      snapshot.docs.reverse().forEach(doc => {
        const data = doc.data();
        newData.push([data.timestamp, data.value]);
      });

      // 初始化或更新图表数据
      if (chart.series[0].data.length === 0) {
        chart.series[0].setData(newData);
      } else {
        // 只添加新增的数据
        const lastTimestamp = chart.series[0].data[chart.series[0].data.length - 1]?.x || 0;
        const addedPoints = newData.filter(point => point[0] > lastTimestamp);
        addedPoints.forEach(point => {
          chart.series[0].addPoint(point, true, true);
        });
      }
    });

    // 组件卸载时取消监听,避免内存泄漏
    return () => unsubscribeRef.current();
  }, []);

  return <Chart ref={chartRef} options={options} />;
};

步骤3:Firestore数据结构说明

你的Firestore集合(建议命名为heartRates)中的文档需要包含两个字段:

  • timestamp: 时间戳(number类型,对应JavaScript的Date.getTime())
  • value: 心率数值(number类型)

测试用的新增数据代码示例:

import { db } from './firebaseConfig';
import { collection, addDoc } from "firebase/firestore";

// 模拟新增心率数据到Firestore
const addHeartRateData = async (value: number) => {
  await addDoc(collection(db, 'heartRates'), {
    timestamp: new Date().getTime(),
    value: value
  });
};

关键说明

  • 用useRef保存图表实例和监听取消函数,避免组件重渲染时重复创建监听
  • onSnapshot会实时接收数据变化,包括初始加载和后续新增数据
  • addPoint方法的第三个参数设为true,会自动移除最早的点,保持图表数据数量稳定(和原模拟逻辑一致)

内容的提问来源于stack exchange,提问作者Jecê Xavier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 08:11:27