React中useState与useEffect未通过Firebase数据更新状态问题
解决React+Firestore数据拉取后map()报错问题
问题情况
部署在Firebase上的React应用,从Firestore拉取数据传给<FairytalesList>组件时,该组件调用map()方法触发错误:
react-dom.production.min.js:189 TypeError: Cannot read properties of undefined (reading 'map')
将videoData初始值设为null后,错误变为无法读取null的属性。通过异步函数getDocs()获取数据更新videoData,查询条件来自newSelectedCategory状态(由<Filters>组件的applyFilter()设置)。已确认Firestore存在目标数据,且在index.js中调用getDocs()可正常返回数据,但不想在该文件执行拉取操作。尝试将useEffect依赖项改为[videoData]无效果。
错误原因
- 初始状态问题:
videoData初始值为undefined/null,组件初始渲染时<FairytalesList>拿到非数组值,调用map()直接报错。 - 依赖项缺失:
useEffect未监听newSelectedCategory变化,筛选条件更新后不会重新拉取数据,状态无法同步。 - 查询参数错误:Firestore的
where条件中,错误地将数组newSelectedCategory包裹成对象传递,导致查询失效。 - 数据格式错误:直接将
getDocs()返回的QuerySnapshot存入videoData,该对象无法直接用map()遍历,需要转换为数组格式。
修复步骤
1. 初始化videoData为空数组
确保组件初始渲染时,<FairytalesList>拿到的是可遍历的空数组:
const [videoData, setVideoData] = useState([]);
2. 修正useEffect依赖项
让useEffect监听newSelectedCategory,筛选条件变化时自动重新拉取数据:
useEffect(() => { fetchVideoData(); }, [newSelectedCategory]) // 依赖改为newSelectedCategory
3. 修复Firestore查询参数
去掉where条件中包裹数组的对象,直接传递newSelectedCategory数组:
if (Array.isArray(newSelectedCategory) && newSelectedCategory.length) { q = query( collection(props.videoDatabase, "videos"), where("categories", "array-contains-any", newSelectedCategory) // 移除花括号 ); }
4. 转换QuerySnapshot为可遍历数组
将getDocs()返回的快照转换为包含文档数据和ID的数组,存入videoData:
getDocs(q) .then(function(snapshot) { // 转换快照为数组格式 const videos = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); setVideoData(videos); })
修复后完整代码示例
export function ExplorePage(props) { const [videoData, setVideoData] = useState([]); // 初始化为空数组 const [alertMessage, setAlertMessage] = useState(null); const [isFetching, setIsFetching] = useState(false); const [newSelectedCategory, setNewSelectedCategory] = useState([]); const fetchVideoData = () => { setIsFetching(true); let q = collection(props.videoDatabase, "videos"); if (Array.isArray(newSelectedCategory) && newSelectedCategory.length) { // 修复查询参数,直接传递数组 q = query(collection(props.videoDatabase, "videos"), where("categories", "array-contains-any", newSelectedCategory)); } getDocs(q) .then(function(snapshot) { // 转换快照为可遍历数组 const videos = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); setVideoData(videos); }) .catch((error) => { console.log(error.message); setAlertMessage(error.message); }) .then(() => { setIsFetching(false); }); } // 依赖项改为newSelectedCategory,筛选变化时重新拉取 useEffect(() => { fetchVideoData(); }, [newSelectedCategory]) const applyFilter = (categoryArray) => { console.log("reached apply filter"); setNewSelectedCategory(categoryArray) } const categoryList = ["food", "fashion", "cosmetics", "home", "transportation", "children", "reduce waste", "clean energy"]; return ( <> <NavBar /> <Filters uniqueCategory={categoryList} applyFilterCallback={applyFilter}/> {isFetching && <p>Loading videos...</p>} {alertMessage && <p className="bg-danger text-light p-3 mb-2">Failed to fetch the data. Error: {alertMessage}</p>} <FairytalesList categoriesQuerySnapshot={videoData}/> <Footer /> </> ); }
内容的提问来源于stack exchange,提问作者user23512968
相关产品推荐
相关产品推荐

