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

React Native集成Firebase:异步调用为何执行两次?

React Native Firestore异步函数重复执行、先返回null再返回数据的问题

我在开发React Native应用时遇到一个困惑:两个JS文件交互时,调用Firestore获取数据的异步函数似乎会执行两次。在HomeScreen中调用UserInformation等组件时,控制台先返回null,之后才返回正常数据。其他查询数据库的函数也存在同样问题,不确定是异步代码的问题还是正常行为,求解决办法。


相关代码

HomeScreen.js

import { Pressable, Text } from 'react-native';
import { useEffect, useId, useState } from 'react';
import { async } from '@firebase/util';
import { FlatList } from 'react-native-gesture-handler';
import {getStorage, ref, getDownloadURL} from 'firebase/storage';
//import the user infomration component
import UserInformation from '../components/UserInformation';
import ClubInformation from '../components/ClubInformation';
import AircraftInformation from '../components/AircraftInformation';

function HomeScreen() {
    const userDetails = UserInformation();
    const clubDetails = ClubInformation();
    const aircraftDetails = AircraftInformation();
    
    if (userDetails !== null) {
        //console.log(userDetails)
        //console.log(clubDetails)
        //console.log(aircraftDetails)
    }

    if(!userDetails) {  
        return <Text>Loading...</Text>
    }

    if(!clubDetails) {
        return <Text>Loading...</Text>
    }
        
    return <Text>Welcome {userDetails.first} {userDetails.last} {clubDetails.name}!  This is a placeholder for future updates in the home screen</Text>
}

export default HomeScreen

UserInformation.js

import { useState, useEffect } from "react";
import { getAuth } from "firebase/auth";
import { doc, getDoc } from "firebase/firestore";
import { db } from '../components/FirebaseConfig';

export default function UserInformation() {
    const [userDetails, setUserDetails] = useState(null);
    
    useEffect(() => {
        async function getUserDetails() {
        const user = getAuth().currentUser;
        const userRef = doc(db, "users", user.uid);
        const docSnap = await getDoc(userRef);
        if (docSnap.exists()) {
            const data = docSnap.data();
            setUserDetails(data);
        } else {
            console.log("No such User!");
        }
        }
        getUserDetails();
    }, []);
    
    return userDetails;
}

问题原因与解决办法

核心问题

  1. 组件调用方式错误:你把UserInformation这类React组件当作普通函数直接调用,而非通过JSX渲染。这种写法会绕过React的组件生命周期机制,导致每次HomeScreen渲染时,子组件内的useState和useEffect都会重新执行,进而重复触发Firestore请求。
  2. 异步加载的正常特性:初始返回null是异步数据加载的必然阶段——数据请求未完成时,组件初始状态就是null,请求结束后才会更新状态返回真实数据,但错误的调用方式放大了重复执行的问题。

修复步骤

1. 修正HomeScreen的组件调用方式

React组件必须通过JSX语法渲染,同时通过状态接收子组件传递的数据:

import { Text } from 'react-native';
import { useState } from 'react';
import UserInformation from '../components/UserInformation';
import ClubInformation from '../components/ClubInformation';

function HomeScreen() {
    const [userData, setUserData] = useState(null);
    const [clubData, setClubData] = useState(null);

    return (
        <>
            {/* 以组件形式渲染,通过props传递回调获取数据 */}
            <UserInformation onDataLoaded={setUserData} />
            <ClubInformation onDataLoaded={setClubData} />

            {/* 统一处理加载状态 */}
            {!userData || !clubData ? (
                <Text>Loading...</Text>
            ) : (
                <Text>Welcome {userData.first} {userData.last} {clubData.name}!  This is a placeholder for future updates in the home screen</Text>
            )}
        </>
    );
}

export default HomeScreen;

2. 修改子组件,通过回调传递数据

让子组件作为真正的React组件工作,将获取到的数据通过props传递给父组件:

import { useState, useEffect } from "react";
import { getAuth } from "firebase/auth";
import { doc, getDoc } from "firebase/firestore";
import { db } from '../components/FirebaseConfig';

export default function UserInformation({ onDataLoaded }) {
    const [userDetails, setUserDetails] = useState(null);
    
    useEffect(() => {
        let isMounted = true; // 防止组件卸载后更新状态
        
        async function getUserDetails() {
            const user = getAuth().currentUser;
            if (!user || !isMounted) return;
            
            const userRef = doc(db, "users", user.uid);
            const docSnap = await getDoc(userRef);
            
            if (docSnap.exists() && isMounted) {
                const data = docSnap.data();
                setUserDetails(data);
                onDataLoaded(data); // 把数据传递给父组件
            } else {
                console.log("No such User!");
            }
        }
        
        getUserDetails();
        return () => { isMounted = false; }; // 组件卸载时标记状态
    }, [onDataLoaded]);

    // 若仅需获取数据,返回null即可;也可在此渲染子组件自身内容
    return null;
}

3. 额外优化点

  • 为useEffect添加正确的依赖项,确保仅在必要时重新执行请求;
  • 增加isMounted标记,避免组件卸载后还执行异步操作导致内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:56:06