You need to enable JavaScript to run this app. Firebase使用onSnapshot拉取产品数组非空但DOM映射渲染无内容问题
优惠活动
大模型
产品
解决方案
定价
更多

Firebase使用onSnapshot拉取产品数组非空但DOM映射渲染无内容问题

问题原因
  • 异步执行时序问题:onSnapshot 是Firebase的实时监听回调方法,不会阻塞同步代码执行,你定义的getUsersProducts函数会直接返回初始化的空数组UsersProducts,此时回调还没触发,你dispatch到Redux里的是一开始的空数组。
  • Redux状态更新规则被打破:就算后续onSnapshot回调触发,你用push方法直接修改原数组的内容,数组的引用地址没有变化,Redux默认通过浅比较判断状态是否更新,感知不到数组内容的变化,因此不会触发组件重渲染,所以DOM不会更新。你看到控制台打印有值是因为浏览器控制台打印引用类型时会取最新值,不是你dispatch那一刻的实际值。
  • 缺失监听取消逻辑:onSnapshot会返回一个取消订阅的函数,你没有处理的话会导致组件卸载后监听还在,触发内存泄漏。
修复方案

1. 重构数据监听方法,在回调中触发状态更新

// 接收uid和dispatch作为参数,返回取消订阅的函数
export const subscribeUsersProducts = (uid, dispatch) => {
  return db.collection('products').where("userID", "==", uid).onSnapshot(querySnapshot => {
    // 每次数据更新都生成新数组,符合Redux不可变更新要求
    const UsersProducts = []
    querySnapshot.docChanges().forEach(change => {
      if (change.type === "added") {
        // 把文档id也存入数据,否则渲染时key={product.id}会取不到值
        UsersProducts.push({...change.doc.data(), id: change.doc.id})
      }
      // 后续需要支持产品修改、删除能力的话,在这里补充change.type为modified、removed的处理逻辑
    })
    // 新数组dispatch到store,会触发Redux状态更新
    dispatch(setUsersProducts(UsersProducts))
  })
}

2. 调整组件调用逻辑,处理订阅生命周期

const MyProducts = () => {
    const CurrentUserInfos = useSelector(state => state.userReducer.currentUserInfos)
    const searchQuery = useSelector(state => state.productsReducer.searchQuery)
    const myProducts = useSelector(state => state.productsReducer.usersProducts)
    const dispatch = useDispatch()

    useEffect(() => {
        if (!CurrentUserInfos?.userID) return
        // 开启实时监听,拿到取消订阅的方法
        const unsubscribe = subscribeUsersProducts(CurrentUserInfos.userID, dispatch)
        // 组件卸载时自动取消监听,避免内存泄漏
        return () => unsubscribe()
    }, [CurrentUserInfos, dispatch])

    return (
        <div className="my-products">
            <div className="my-products__search-bar">
                <SearchBar />
            </div>
            <div className="my-products__list">
                {
                    myProducts.length === 0 ? (<Loading />) : (myProducts.filter(product => {
                        if(searchQuery==="")
                            return true
                        return product.title?.toLowerCase().includes(searchQuery.toLowerCase())
                    }).map(product => {
                        return(
                            <ProductItem
                                key={product.id}
                                product={product}
                            />
                        )
                    }))
                }
            </div>
        </div>
    )
}

export default MyProducts

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 10:54:00