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; }
问题原因与解决办法
核心问题
- 组件调用方式错误:你把
UserInformation这类React组件当作普通函数直接调用,而非通过JSX渲染。这种写法会绕过React的组件生命周期机制,导致每次HomeScreen渲染时,子组件内的useState和useEffect都会重新执行,进而重复触发Firestore请求。 - 异步加载的正常特性:初始返回
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
相关产品推荐
相关产品推荐

