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

React Native + Firestore问题:如何在国家视图展示城市人口数

获取Firestore子集合数据并在React Native视图中展示

问题背景

Firestore数据库结构:state集合 → 子集合city,city文档包含people字段存储人口数。需要在国家(state)视图中展示每个state下属城市的总人口数。

现有代码问题

  • 嵌套onSnapshot导致数据更新不同步,全局状态itemPeople无法对应单个state
  • 未清理监听订阅,易造成内存泄漏
  • 数据计算与状态更新逻辑分离,渲染时可能无有效人口数据

优化解决方案

方案1:一次性获取数据(适合数据不频繁更新场景)

通过Promise.all并行获取每个state下的city数据,计算总人口后统一更新状态:

import { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import firestore from '@react-native-firebase/firestore';

const StatePopulationScreen = () => {
  const [stateData, setStateData] = useState([]);

  // 获取单个state及其下属城市的总人口
  const getStateWithPopulation = async (stateDoc) => {
    const stateInfo = { ...stateDoc.data(), id: stateDoc.id };
    // 查询当前state下的所有city
    const citySnapshot = await firestore()
      .collection('state')
      .doc(stateDoc.id)
      .collection('city')
      .get();
    // 计算总人口
    const totalPeople = citySnapshot.docs.reduce((sum, doc) => {
      return sum + (doc.data().people || 0);
    }, 0);
    return { ...stateInfo, totalPeople };
  };

  const getData = () => {
    // 监听state集合变化,实时更新数据
    const unsubscribe = firestore()
      .collection('state')
      .onSnapshot(async (querySnapshot) => {
        const stateList = await Promise.all(
          querySnapshot.docs.map(doc => getStateWithPopulation(doc))
        );
        setStateData(stateList);
      }, (error) => {
        console.error('数据获取失败:', error);
      });

    return unsubscribe;
  };

  useEffect(() => {
    const unsubscribe = getData();
    // 组件卸载时清理监听
    return () => unsubscribe();
  }, []);

  // 渲染视图
  return (
    <View style={{ flex: 1, padding: 16 }}>
      {stateData.map(state => (
        <View 
          key={state.id} 
          style={{ paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#e0e0e0' }}
        >
          <Text style={{ fontSize: 18, fontWeight: '600' }}>{state.name}</Text>
          <Text style={{ marginTop: 4, color: '#666' }}>总人口:{state.totalPeople}</Text>
        </View>
      ))}
    </View>
  );
};

export default StatePopulationScreen;

方案2:实时监听子集合变化(适合数据频繁更新场景)

为每个state下的city集合添加独立监听,实现人口数据实时刷新:

import { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import firestore from '@react-native-firebase/firestore';

const StatePopulationScreen = () => {
  const [stateData, setStateData] = useState([]);

  // 监听单个state下的city变化,更新总人口
  const setupCityListener = (stateDoc) => {
    return firestore()
      .collection('state')
      .doc(stateDoc.id)
      .collection('city')
      .onSnapshot((citySnapshot) => {
        const totalPeople = citySnapshot.docs.reduce((sum, doc) => {
          return sum + (doc.data().people || 0);
        }, 0);
        // 更新对应state的人口数据
        setStateData(prevList => prevList.map(state => 
          state.id === stateDoc.id ? { ...state, totalPeople } : state
        ));
      });
  };

  const getData = () => {
    const cityUnsubscribes = [];
    // 监听state集合的新增/删除
    const unsubscribeStates = firestore()
      .collection('state')
      .onSnapshot((querySnapshot) => {
        // 清理之前的city监听
        cityUnsubscribes.forEach(unsub => unsub());
        cityUnsubscribes.length = 0;

        // 初始化state列表
        const initialStates = querySnapshot.docs.map(doc => ({
          ...doc.data(),
          id: doc.id,
          totalPeople: 0
        }));
        setStateData(initialStates);

        // 为每个state设置city监听
        querySnapshot.docs.forEach(doc => {
          const unsub = setupCityListener(doc);
          cityUnsubscribes.push(unsub);
        });
      }, (error) => {
        console.error('数据获取失败:', error);
      });

    // 返回总清理函数
    return () => {
      unsubscribeStates();
      cityUnsubscribes.forEach(unsub => unsub());
    };
  };

  useEffect(() => {
    const unsubscribe = getData();
    return () => unsubscribe();
  }, []);

  // 渲染视图同方案1
  return (
    <View style={{ flex: 1, padding: 16 }}>
      {stateData.map(state => (
        <View 
          key={state.id} 
          style={{ paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#e0e0e0' }}
        >
          <Text style={{ fontSize: 18, fontWeight: '600' }}>{state.name}</Text>
          <Text style={{ marginTop: 4, color: '#666' }}>总人口:{state.totalPeople}</Text>
        </View>
      ))}
    </View>
  );
};

export default StatePopulationScreen;

关键改进点

  • 每个state对象携带对应总人口,直接绑定视图渲染,避免全局状态混乱
  • 保留onSnapshot实现数据实时同步,同时添加监听清理逻辑,防止内存泄漏
  • 使用reduce简化人口总和计算,代码更简洁高效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:15:48