Firestore查询中如何正确声明变量,避免Query.where传undefined报错
报错根因
两个空依赖的useEffect会在组件首次渲染完成后按顺序立刻触发,此时videos集合的异步查询还在等待返回,video状态仍是初始的空数组:
- 执行第二个查询逻辑时,
video.map(v => v.uploadBy)生成的showUrl是空数组,取showUrl[0]必然是undefined - 把
undefined直接传给Firestore的.where()方法,就会直接抛出参数无效的报错
你在控制台能看到showUrl[0]的正常值,是videos查询返回、video状态更新触发重渲染时,重新计算的showUrl才有值,但第二个查询早在首次渲染时就已经带着undefined参数发出去了,根本不会等正确值就绪再执行。
热更新时能正常运行,是因为热更新不会清空组件已有状态,video已经是之前加载完成的有效数据,第二个查询执行时参数自然正常,刷新页面状态重置后就会直接复现报错。
修复方案
- 把跨查询需要用到的派生变量做状态关联,给第二个
useEffect加上正确的依赖值,让它只有在依赖拿到有效数据时才触发查询 - 给所有Firestore查询加前置判断,只有拿到有效的查询参数时才发请求,避免无效参数触发报错
修正后的可运行代码参考:
let { id } = useParams(); const [video, setVideo] = React.useState([]); const [show, setShow] = React.useState([]); const classes = useStyles(); // 首次加载匹配的video数据 React.useEffect(() => { // 路由参数未加载完成时直接跳过,避免无效查询 if (!id) return; const fetchVideo = async () => { const db = firebase.firestore(); const data = await db .collection("videos") .where('path', '==', id) .get() setVideo(data.docs.map(doc => doc.data())); } fetchVideo() }, [id]) // 从video数据里同步派生查询需要的参数 const showUrl = video.map(item => item.uploadBy); const targetShowUrl = showUrl[0]; // 你的日期计算逻辑也可以在这里加兜底,避免空值报错 const videoDate = video.map(item => item.date?.toDate()); console.log(videoDate[0]); // 等拿到有效的showUrl参数再查询shows集合 React.useEffect(() => { // 没有拿到有效目标值直接中断,避免where参数无效 if (!targetShowUrl) return; const fetchShow = async () => { const db = firebase.firestore(); const data = await db .collection("shows") .where('urlPath', '==', targetShowUrl) .get() setShow(data.docs.map(doc => doc.data())); } fetchShow() }, [targetShowUrl])
避坑提示
- 不要给跨异步查询的逻辑用空依赖
useEffect,React的状态更新是异步的,必须把effect依赖的外部值明确写到依赖数组里,才能保证执行时序符合预期 - 处理Firestore返回的时间戳字段时,建议加上可选链
?.,避免遇到文档缺失字段时直接抛出空值调用错误 - 所有
.where()的查询参数,一定要在发请求前做非空校验,Firestore对undefined、null以外的空值校验非常严格,参数不符会直接抛同步错误
内容的提问来源于stack exchange,提问作者tmst
相关产品推荐
相关产品推荐

