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

React结合Firebase:初始渲染仅加载user,如何实现双数据预加载?

React + Firebase 双数据自动加载解决方案

问题背景

开发基于Firebase数据库的React应用时,需要加载user和list两个独立数据对象并展示。目前存在的问题是:初始渲染仅user对象能正常加载显示,list数据必须手动刷新页面才会出现,希望实现无需刷新即可自动加载双数据的效果。

当前组件渲染代码

const [ dadosCarregados, setDadosCarregados ] = useState(false);
const { retrieveUser, getUserSheets, user, list } = useRetrieveDatabase()

useEffect(() => {
  retrieveUser(userId).then(
    getUserSheets(userId).then(
      setDadosCarregados(true)
    )
  )
}, [dadosCarregados])

if(!user) {
  return <p>Carregando...</p>
}

return (//the loaded page)

数据获取Hook代码

const retrieveUser = async (userId) => {
        checkIfIsCancelled()
        setLoading(true)

        try {
            const userDocRef = doc(collection(db, 'usuarios'), userId)
            const userDocSnapshot = await getDoc(userDocRef)

            if (userDocSnapshot.exists()) {
                setUser(userDocSnapshot.data())
            } else {
                console.log('Usuario não encontrado')
            }
        
        } catch (error) {
            console.log('Erro ao buscar usuário: ' + error)
        } finally {
            setLoading(false)
        }
    }

const getUserSheets = async (userId) => {
        checkIfIsCancelled()
        setLoading(true)

        try {
            const sheetsQuery = query(collection(db, 'fichas'), where('Idcriador', '==', userId));
            const sheetsQuerySnapshot = await getDocs(sheetsQuery);

            const userSheets = sheetsQuerySnapshot.docs.map((doc) => doc.data());
            setList(userSheets);
        
        } catch (error) {
            console.log('Erro: ' + error)
            return []
        } finally {
            setLoading(false)
        }
    }

尝试过合并数据获取函数、在判断条件中加入dadosCarregados和list均未解决问题,甚至引发报错。


问题根源分析

  1. useEffect依赖逻辑错误:将dadosCarregados作为依赖项,初始执行后设置为true会触发useEffect重复执行,造成无效请求;同时异步函数链式调用写法错误,未等待retrieveUser完成就执行getUserSheets,且提前标记数据加载完成。
  2. 加载判断不完整:仅检查user是否存在,list未加载完成就渲染页面,导致初始渲染时list为空。
  3. Hook内loading状态冲突:两个异步请求共享同一个loading状态,retrieveUser完成后将loading设为false,但此时getUserSheets可能仍在加载,导致状态混乱。

解决方案

方案1:并行请求+完整加载判断

用Promise.all并行执行两个异步请求,修改useEffect依赖为userId,确保用户ID变化时重新请求;同时判断user和list都加载完成后再渲染页面:

// 移除冗余的dadosCarregados状态,复用Hook内的loading或直接判断数据
const { retrieveUser, getUserSheets, user, list } = useRetrieveDatabase()

useEffect(() => {
  // 并行执行两个请求,等待全部完成
  Promise.all([
    retrieveUser(userId),
    getUserSheets(userId)
  ])
  .catch(err => console.error('数据加载失败:', err))
}, [userId, retrieveUser, getUserSheets])

// 同时检查user和list是否就绪
if (!user || !list) {
  return <p>Carregando...</p>
}

return (//the loaded page)

方案2:优化Hook的loading状态

在Hook内为两个请求分别设置独立的loading状态,避免状态冲突,再导出合并的loading状态供组件使用:

// Hook内部新增独立loading状态
const [userLoading, setUserLoading] = useState(false);
const [sheetsLoading, setSheetsLoading] = useState(false);

const retrieveUser = async (userId) => {
  checkIfIsCancelled()
  setUserLoading(true)

  try {
    // 原有逻辑不变
    const userDocRef = doc(collection(db, 'usuarios'), userId)
    const userDocSnapshot = await getDoc(userDocRef)
    if (userDocSnapshot.exists()) setUser(userDocSnapshot.data())
    else console.log('Usuario não encontrado')
  } catch (error) {
    console.log('Erro ao buscar usuário: ' + error)
  } finally {
    setUserLoading(false)
  }
}

const getUserSheets = async (userId) => {
  checkIfIsCancelled()
  setSheetsLoading(true)

  try {
    // 原有逻辑不变
    const sheetsQuery = query(collection(db, 'fichas'), where('Idcriador', '==', userId));
    const sheetsQuerySnapshot = await getDocs(sheetsQuery);
    const userSheets = sheetsQuerySnapshot.docs.map(doc => doc.data());
    setList(userSheets);
  } catch (error) {
    console.log('Erro: ' + error)
    return []
  } finally {
    setSheetsLoading(false)
  }
}

// 导出合并的loading状态
const loading = userLoading || sheetsLoading;
return { retrieveUser, getUserSheets, user, list, loading };

组件中使用合并的loading状态判断:

const { retrieveUser, getUserSheets, user, list, loading } = useRetrieveDatabase()

useEffect(() => {
  retrieveUser(userId);
  getUserSheets(userId);
}, [userId, retrieveUser, getUserSheets])

if (loading) {
  return <p>Carregando...</p>
}

// 额外兜底判断,避免空数据
if (!user || !list.length) {
  return <p>Dados não encontrados</p>
}

return (//the loaded page)

方案3:修正异步链式调用逻辑

如果要保留原有状态,需修正链式调用的写法,确保等待每个请求完成,并修改useEffect依赖:

const [ dadosCarregados, setDadosCarregados ] = useState(false);
const { retrieveUser, getUserSheets, user, list } = useRetrieveDatabase()

useEffect(() => {
  // 正确等待retrieveUser完成后再执行getUserSheets
  retrieveUser(userId)
    .then(() => getUserSheets(userId))
    .then(() => setDadosCarregados(true))
    .catch(err => console.error('加载失败:', err))
}, [userId, retrieveUser, getUserSheets]) // 依赖改为userId和请求函数,而非dadosCarregados

// 同时检查所有加载条件
if (!user || !list || !dadosCarregados) {
  return <p>Carregando...</p>
}

return (//the loaded page)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:52:03