React中Firestore数据渲染异常:首页数据需跳转后才加载
React应用Firestore数据加载异常问题排查与解决
问题现象
首页的Firestore数据仅在跳转到其他页面后返回首页时才会加载,初始进入首页时拿到的是空数组。
Menu组件代码
function Menu() { const [data, setData] = useState([]); const { user } = UserAuth(); const fetchProduct = async () => { const querySnapshot = await getDocs(collection(db, "langcards-db")); const arr = []; querySnapshot.forEach((doc) => { arr.push({ ...doc.data(), id: doc.id, }); }); setData(arr); } useEffect(() => { if(user){ fetchProduct(); }else{ setData([]) } }, []); useEffect(() => { if(user){ fetchProduct(); }else{ setData([]) } }, [user]); return ( <div className="content"> <Routes> <Route path="/" element={<Layout onSearch={() => onSearchF()} data={data} />} > <Route index element={<Home data={data} num={data.length} />}/> <Route path="saved" element={<Saved data={data} setData={setData} />} /> </Route> </Routes> </div> ); } export default Menu;
Home组件代码
const shuffle = (arr) => arr.sort(() => Math.random() - 0.5); function Home({ data, num }) { const [newArr, setNewArr] = useState(shuffle([...data])); const [i, setI] = useState(0); const randomCard = useMemo(() => { if (!newArr || i < 0 || i >= newArr.length) return undefined; return newArr[i]; }, [newArr, i]); const randomizeCard = () => { if (i < newArr.length - 1) { setI(i + 1); } else { setNewArr(shuffle([...data])); setI(0); } }; return ( <div> <div className="home-inner"> {num >= 1 && randomCard ? ( <div className="home-stat"> <p className='saved-stat'> Saved words: <span>{num}</span> </p> <div onClick={randomizeCard} className="home-random"> <div className="hr-card"> <p className="randomword">{randomCard.word}</p> <p className="randomtranslate">{randomCard.translate}</p> </div> </div> <Link className="home-btn main-btn" to="/saved"> See all </Link> </div> ) : ( <div> {" "} <div className="oops-section"> <Link className="main-btn" to="/addcard"> Add first word </Link> </div> </div> )} </div> </div> ); }
代码错误排查
Menu组件存在冗余useEffect
两个逻辑完全一致的useEffect,一个依赖空数组,一个依赖user,会导致初始渲染时重复发起数据请求,属于冗余代码。Home组件newArr未响应data更新
newArr的初始值仅在组件第一次挂载时计算,当Menu组件中的data从空数组加载完成后,Home组件的newArr不会自动更新。只有跳转回来重新挂载Home时,才会用新的data初始化newArr,这就是初始首页不显示数据的核心原因。
解决方案
1. 合并Menu组件的useEffect
保留一个依赖user的useEffect即可,初始渲染时也会执行:
useEffect(() => { if(user){ fetchProduct(); }else{ setData([]) } }, [user]);
2. 让Home组件newArr响应data变化
添加useEffect监听data变化,自动更新洗牌后的数组:
function Home({ data, num }) { const [newArr, setNewArr] = useState(shuffle([...data])); const [i, setI] = useState(0); // 监听data变化,重新洗牌并重置索引 useEffect(() => { setNewArr(shuffle([...data])); setI(0); }, [data]); const randomCard = useMemo(() => { if (!newArr || i < 0 || i >= newArr.length) return undefined; return newArr[i]; }, [newArr, i]); // 其余代码不变 }
3. 可选:添加加载状态优化体验
在Menu组件中加入加载状态,避免初始显示错误提示:
function Menu() { const [data, setData] = useState([]); const [isLoading, setIsLoading] = useState(true); const { user } = UserAuth(); const fetchProduct = async () => { setIsLoading(true); try { const querySnapshot = await getDocs(collection(db, "langcards-db")); const arr = []; querySnapshot.forEach((doc) => { arr.push({ ...doc.data(), id: doc.id, }); }); setData(arr); } catch (err) { console.error("加载数据失败:", err); } finally { setIsLoading(false); } } useEffect(() => { if(user){ fetchProduct(); }else{ setData([]); setIsLoading(false); } }, [user]); return ( <div className="content"> <Routes> <Route path="/" element={<Layout onSearch={() => onSearchF()} data={data} />} > <Route index element={<Home data={data} num={data.length} isLoading={isLoading} />}/> <Route path="saved" element={<Saved data={data} setData={setData} />} /> </Route> </Routes> </div> ); }
在Home组件中处理加载状态:
function Home({ data, num, isLoading }) { if (isLoading) { return <div>加载中...</div>; } // 原渲染内容 }
内容的提问来源于stack exchange,提问作者wch
相关产品推荐
相关产品推荐

