You need to enable JavaScript to run this app. React对接Firestore实时数据触发过量调用致应用崩溃问题求助
优惠活动
大模型
产品
解决方案
定价
更多

React对接Firestore实时数据触发过量调用致应用崩溃问题求助

问题排查与修复方案

核心问题定位

  • 你在RuuviTag.map渲染逻辑中直接调用checkBoundaries方法,该方法每次执行都会为指定MAC的Firestore文档注册一个全新的onSnapshot实时监听器,且从未做卸载清理。只要组件重渲染(比如state更新触发重绘),就会新增一批监听器,监听器数量指数级上涨,自然会产生海量日志、频繁拉取数据最终导致应用崩溃。
  • 你已经通过getData方法监听了整个RuuviTag集合的实时变更,再给每个文档单独加监听器属于完全冗余的重复操作,没有任何必要。
  • responseData、mapIcon是组件全局共享的state,所有标签会共用同一份数据,无法实现单个标签根据自身温度单独变色的需求。
  • axios.post是异步请求,你在请求发出后立刻读取responseData,此时请求还未返回,拿到的永远是旧值,温度判断逻辑完全失效。
  • 没有对Firestore监听器做卸载处理,组件销毁后监听器仍然在运行,会造成内存泄漏。

修复代码

import './LiveRouteTile.scss';
import { useState, useEffect } from 'react';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import { Icon } from 'leaflet'
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'
import firebase from "../../../firebase"
import axios from "axios";

// 图标地址抽成常量避免重复声明
const RED_ICON = 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png';
const BLUE_ICON = 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-blue.png';

const LiveRouteTile = ({ type }) => {
  const [tagList, setTagList] = useState([]);
  const [loading, setLoading] = useState(false);
  // 存储每个MAC对应的接口返回数据和图标,避免全局共享
  const [tagMetaMap, setTagMetaMap] = useState({});

  // 处理单条tag的接口请求和温度判断
  const processTagData = async (tag) => {
    try {
      const res = await axios.post("https://localhost:44332/Convert/", { rawData: tag.RawData });
      const temp = res.data.temperature;
      const icon = temp > 4 && temp < 22 ? BLUE_ICON : RED_ICON;
      // 只更新当前MAC对应的元数据
      setTagMetaMap(prev => ({
        ...prev,
        [tag.MAC]: {
          data: res.data,
          icon
        }
      }));
    } catch (err) {
      console.error(`处理MAC为${tag.MAC}的标签数据失败:`, err);
    }
  };

  useEffect(() => {
    setLoading(true);
    const collectionRef = firebase.firestore().collection("RuuviTag");
    // 保存监听器取消函数
    const unsubscribe = collectionRef.onSnapshot((querySnapshot) => {
      const items = [];
      querySnapshot.forEach((doc) => {
        const tagData = doc.data();
        items.push(tagData);
        // 集合数据更新时处理当前tag的请求逻辑,无需单独监听文档
        processTagData(tagData);
      });
      setTagList(items);
      setLoading(false);
    });

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

  return (
    <div className="route-tile">
      <Card className="route-card" >
        <CardContent className="route-card-content">
          {loading ? <h1>Loading...</h1> : (
            <MapContainer className="map" center={[52.09, 5.10]} zoom={8}>
              <TileLayer
                attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
              />
              {tagList.map((tag) => {
                const meta = tagMetaMap[tag.MAC] || { icon: RED_ICON };
                return (
                  <div key={tag.MAC}>
                    <Marker 
                      position={[tag.Latitude.replace(/,/g, '.'), tag.Longitude.replace(/,/g, '.')]} 
                      icon={new Icon({ iconUrl: meta.icon, iconSize: [25, 41], iconAnchor: [12, 41] })} 
                    >
                      <Popup>
                        <h1>{tag.MAC}</h1>
                        {meta.data && (
                          <div>
                            <p>温度:{meta.data.temperature}℃</p>
                            <p>湿度:{meta.data.humidity}%</p>
                          </div>
                        )}
                      </Popup>
                    </Marker>
                  </div>
                )
              })}
            </MapContainer>
          )}
        </CardContent>
      </Card>
    </div>
  )
}

export default LiveRouteTile

优化点说明

  • 移除了单文档的重复监听器,仅保留集合级别的实时监听,大幅降低Firestore请求量
  • 每个标签的接口返回数据、图标状态独立存储,互不影响
  • 所有副作用逻辑全部移到useEffect中,不会在渲染时重复执行
  • 新增了监听器清理逻辑,避免内存泄漏
  • 异步请求使用async/await改写,逻辑更清晰,不会出现时序问题

内容的提问来源于stack exchange,提问作者Jaap Smit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 01:36:01