You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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>
  );
}

额外说明

  1. 用await替代then链式调用,代码更易读且符合async/await的最佳实践
  2. 抛出错误让React Query统一管理错误状态,无需手动处理
  3. 移除冗余的本地状态后,组件逻辑更简洁,同时利用React Query的缓存、重试等特性
  4. 调整Swiper的key为数据长度,避免数组引用作为key导致的不必要重渲染

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 06:15:33