将useEffect替换为@tanstack/react-query时的Firestore数据获取问题
问题分析与修复方案
核心错误点
- fetchSongs函数未返回数据:原抽离的函数处理完Firestore数据后没有返回结果,导致React Query无法获取有效数据
- useQuery传入了函数执行结果而非函数引用:你传了
fetchSongs()(直接执行函数),但React Query需要的是函数本身,由它来控制执行时机 - 组件渲染流程中直接调用setState:渲染时调用
setSongData(data)会触发无限重渲染循环 - 冗余本地状态:React Query已经帮你管理数据状态,无需额外用
useState维护songData
修正后的fetchSongs函数
import { getDocs, collection } from "firebase/firestore"; import { db } from "../pages/api/auth/firebase/config"; const fetchSongs = async () => { const colRef = collection(db, "songs"); try { // 用await替代then链式调用,代码更简洁 const snapshot = await getDocs(colRef); // 用map替代forEach,直接生成数组 const data = snapshot.docs.map(doc => ({ ...doc.data(), songId: doc.id, })); return data; // 必须返回处理后的数据给React Query } catch (error) { console.error("获取歌曲数据失败:", error); throw error; // 抛出错误,让React Query捕获并设置error状态 } }; export default fetchSongs;
修正后的Slider组件
import { Swiper, SwiperSlide } from "swiper/react"; import { Virtual, Navigation, FreeMode } from "swiper"; import { SongCard } from "./Songcard"; import Link from "next/link"; import { useQuery } from "@tanstack/react-query"; import fetchSongs from "../utils/getSongs"; import "swiper/css"; import "swiper/css/virtual"; import "swiper/css/navigation"; import "swiper/css/free-mode"; export default function Slider() { // useQuery第二个参数传函数引用,不要加();推荐用数组形式的queryKey const { data: songData, isLoading, error } = useQuery({ queryKey: ["songs"], queryFn: fetchSongs, }); if (isLoading) { return <p>加载中...</p>; } if (error) { return <p>错误: {error.message}</p>; } // 直接使用React Query返回的songData,无需本地状态 return ( <Swiper key={songData?.length} // 用数据长度做key,避免引用类型key的问题 modules={[Virtual, Navigation, FreeMode]} slidesPerView={6} slidesOffsetBefore={-106} virtual navigation={true} grabCursor={true} breakpoints={{ 280: { slidesPerView: 1 }, 350: { slidesPerView: 2, spaceBetween: 50 }, 500: { slidesPerView: 2 }, 638: { slidesPerView: 3 }, 820: { slidesPerView: 4 }, 1060: { slidesPerView: 5 }, 1260: { slidesPerView: 6 }, }} > {songData.map((song) => ( <SwiperSlide key={song.songId}> <Link href={`/artist/${song.userId}`} passHref> <SongCard data={song} /> </Link> </SwiperSlide> ))} </Swiper> ); }
额外说明
- 用
await替代then链式调用,代码更易读且符合async/await的最佳实践 - 抛出错误让React Query统一管理错误状态,无需手动处理
- 移除冗余的本地状态后,组件逻辑更简洁,同时利用React Query的缓存、重试等特性
- 调整Swiper的key为数据长度,避免数组引用作为key导致的不必要重渲染
内容的提问来源于stack exchange,提问作者Yan
相关产品推荐
相关产品推荐

