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均未解决问题,甚至引发报错。
问题根源分析
- useEffect依赖逻辑错误:将
dadosCarregados作为依赖项,初始执行后设置为true会触发useEffect重复执行,造成无效请求;同时异步函数链式调用写法错误,未等待retrieveUser完成就执行getUserSheets,且提前标记数据加载完成。 - 加载判断不完整:仅检查
user是否存在,list未加载完成就渲染页面,导致初始渲染时list为空。 - 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
相关产品推荐
相关产品推荐

