如何解决React Native首次重载时useState数组为空的问题?
React Native 应用重载时useState数组为空的问题解决
问题概况
应用启动重载时,users状态数组为空,界面无内容显示;仅在VS Code保存代码触发热重载后,才能正常加载并展示收藏的freelancer列表。尝试两种useEffect写法均存在问题:
- 无依赖项的useEffect无法正确获取数据
- 添加
users作为依赖项后触发无限循环
原代码问题分析
- 异步逻辑处理错误:原代码中
onSnapshot是异步实时监听回调,querySnapshot.forEach内的async函数不会被forEach等待,导致setUsers(userArray)执行时,userArray还未被填充数据。 - 实时监听误用:若仅需一次性获取数据,使用
onSnapshot会导致回调延迟触发,初始渲染时无法拿到数据;若需实时监听,原代码未正确处理多次回调的状态更新逻辑。 - 依赖项不合理:将
users加入依赖项后,每次setUsers都会更新users状态,进而触发useEffect重复执行,形成无限循环。
修复方案
方案1:一次性获取数据(无需实时更新)
使用getDoc替代onSnapshot,通过Promise.all等待所有用户数据获取完成后再更新状态,同时用useRef标记是否已获取数据,避免重复执行。
import { useEffect, useState, useRef } from 'react'; import { getDocs, query, collection, where, getDoc, doc } from 'firebase/firestore'; // ... 组件其他代码 const [users, setUsers] = useState([]); const isFetched = useRef(false); // 标记数据是否已获取 useEffect(() => { const fetchFavUsers = async () => { if (isFetched.current) return; try { // 查询当前用户的收藏列表 const q = query( collection(db, "favoritFreelancer"), where("cid", "==", userUid) ); const querySnapshot = await getDocs(q); // 批量获取收藏用户的详细数据 const userPromises = querySnapshot.docs.map(async (favUser) => { const userDoc = await getDoc(doc(db, "users", favUser.data().lancerID)); return userDoc.data(); }); const userArray = await Promise.all(userPromises); setUsers(userArray); isFetched.current = true; } catch (error) { console.error("获取收藏用户失败:", error); } }; fetchFavUsers(); }, [userUid]); // 仅依赖userUid,避免无限循环
方案2:实时监听用户数据变化
若需要在用户数据更新时自动刷新界面,需正确管理onSnapshot的监听回调,并在组件卸载时清理监听,同时处理状态的增量更新。
import { useEffect, useState, useRef } from 'react'; import { getDocs, query, collection, where, onSnapshot, doc } from 'firebase/firestore'; // ... 组件其他代码 const [users, setUsers] = useState([]); const unsubscribeRef = useRef([]); // 保存监听取消函数 useEffect(() => { const setupFavUserListeners = async () => { try { // 查询当前用户的收藏列表 const q = query( collection(db, "favoritFreelancer"), where("cid", "==", userUid) ); const querySnapshot = await getDocs(q); querySnapshot.docs.forEach((favUser) => { // 为每个收藏用户添加实时监听 const unsubscribe = onSnapshot(doc(db, "users", favUser.data().lancerID), (userDoc) => { const userData = userDoc.data(); // 增量更新users状态,避免覆盖已有数据 setUsers(prevUsers => { const updatedUsers = [...prevUsers]; const existingIndex = updatedUsers.findIndex(u => u.uid === userData.uid); if (existingIndex !== -1) { updatedUsers[existingIndex] = userData; } else { updatedUsers.push(userData); } return updatedUsers; }); }); unsubscribeRef.current.push(unsubscribe); }); } catch (error) { console.error("设置收藏用户监听失败:", error); } }; setupFavUserListeners(); // 组件卸载时清理所有监听 return () => { unsubscribeRef.current.forEach(unsubscribe => unsubscribe()); }; }, [userUid]);
映射代码修复
原映射代码中第二个条件判断的fullname未指定为user.fullname,会导致报错,修正后代码如下:
<ScrollView style={{ flex: 1 }} className="flex-col w-full"> {users.map((user) => searchText === "" ? ( <FreelancerCard key={user.uid} fullname={user.fullname} experience={user.experience} stars={user.stars} jobs={user.jobs} status={user.status} categories={user.category} contact={user.contact} imgUrl={user.profilePic} /> ) : user.fullname.toLowerCase().includes(searchText.toLowerCase()) ? ( <FreelancerCard key={user.uid} fullname={user.fullname} experience={user.experience} stars={user.stars} jobs={user.jobs} status={user.status} categories={user.category} contact={user.contact} imgUrl={user.profilePic} /> ) : null )} </ScrollView>
验证效果
应用启动重载时,会自动查询并加载收藏的freelancer数据,无需等待热重载即可正常显示界面;同时避免了无限循环问题。
内容的提问来源于stack exchange,提问作者Mariyan Anjelo
相关产品推荐
相关产品推荐

