Firebase useEffect中onSnapshot先返回空数组再返回完整数据问题
问题原因
你的代码存在3个核心问题,才会导致空数组多次返回的情况:
- 定义了
loading状态但从未使用,没有在数据加载完成前拦截对应渲染逻辑,初始空数组会被直接返回给组件渲染 firebase.auth().currentUser不是响应式取值,页面首次加载时Firebase Auth还未完成初始化,此时拿到的currentUser大概率为null,且你的useEffect依赖数组为空,后续鉴权状态更新后也不会重新建立Firestore监听- Firestore的
onSnapshot实时监听首次触发时,需要等待本地缓存读取/网络请求返回数据,初始的items空数组会被先返回,属于状态流设计缺失而非功能bug
修正方案
不需要额外做渲染前预加载,只要把状态监听逻辑和加载状态维护正确即可:
- 先监听Auth状态变化,确认用户登录态有效、拿到合法uid之后再建立Firestore订阅
- 正确维护
loading状态,数据加载完成前组件侧可以展示加载占位,不要直接使用空的items数据 - 所有副作用的依赖要正确填入依赖数组,避免闭包拿到过期值
如果需要做全局级别的加载拦截,可以在应用根组件监听auth初始化状态,等鉴权完成后再渲染路由内容,对于当前Hook的场景,正确维护loading状态已经足够解决空值问题。
修正后的自定义Hook代码如下:
const useItemsF = () => { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { // 先订阅鉴权状态变化,避免拿到未初始化的null用户 const authUnsubscribe = firebase.auth().onAuthStateChanged(user => { // 未登录状态重置数据 if (!user) { setItems([]); setLoading(false); return; } // 拿到有效用户ID后再建立Firestore实时监听 const firestoreUnsubscribe = firebase .firestore() .collection("user") .where("id", "==", user.uid) .onSnapshot(snapshot => { const listItemsUsers = snapshot.docs.map(doc => ({ ProfilePic: doc.get("userProfilePic") })); setItems(listItemsUsers); setLoading(false); }); // 鉴权状态变化/组件卸载时,取消已有Firestore订阅 return () => firestoreUnsubscribe(); }) // 组件卸载时取消鉴权状态订阅 return () => authUnsubscribe(); }, []); return { items, loading }; };
组件侧使用方式
调用Hook时优先判断加载状态,数据就绪后再渲染业务内容,就不会出现空数组多次返回、页面闪烁的问题:
const UserProfile = () => { const { items, loading } = useItemsF(); // 加载中展示占位 if (loading) return <div>加载中...</div> // 数据就绪后渲染业务内容 return ( <div className="profile-list"> {items.map((item, idx) => ( <img src={item.ProfilePic} alt="用户头像" key={idx} /> ))} </div> ) }
内容的提问来源于stack exchange,提问作者Paul VI
相关产品推荐
相关产品推荐

