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

使用React Query调用Firestore时触发Invalid hook call错误排查

解决Next.js中useRouter在数据获取函数里触发的Hook调用错误

问题背景

在Next.js项目中,尝试通过router.query提取URL参数并调用Firestore获取单个文档时遇到问题:此前用相同方式获取Firestore集合(无需路由参数)可正常运行,但当前fetchArtist函数内调用useRouter Hook时触发**"Invalid hook call"**错误,数据返回undefined;但直接调用该函数却能正常获取数据,无法定位违反React Hooks规则的位置。

错误原因

React Hooks的核心规则之一是:只能在React函数组件的顶层,或者自定义Hook的顶层调用Hook。你的fetchArtist是普通异步函数(既不是React组件,也不是自定义Hook),在内部调用useRouter完全违反了这一规则,因此触发错误。

修复方案

将路由参数的获取移到组件顶层,再作为参数传给数据获取函数;同时控制React Query的请求时机,确保路由参数就绪后再发起请求。

修改后的页面组件代码

import React, { useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { useQuery } from "@tanstack/react-query";
import fetchArtist from "../../hooks/getArtists";
import Head from "next/head";
import ArtistHeader from "../../components/ArtistHeader";
import UploadButton from "../../components/UploadButton";
import styles from "../../styles/artistPage.module.css";

export default function Artist() {
  const { status } = useSession();
  const router = useRouter();
  const { artistId } = router.query;

  // 未登录用户跳转至登录页
  useEffect(() => {
    if (status === "unauthenticated") {
      router.push("/auth/signin");
    }
  }, [status, router]);

  // 仅当路由就绪且存在artistId时发起请求
  const { data, status: artistStatus } = useQuery(
    ["artist", artistId], // 将artistId加入查询键,参数变化时自动重新请求
    () => fetchArtist(artistId),
    {
      staleTime: 1000 * 60 * 60 * 24,
      cacheTime: 1000 * 60 * 60 * 24,
      enabled: router.isReady && !!artistId, // 控制请求触发时机
    }
  );

  const [artist, setArtist] = useState();
  useEffect(() => {
    if (artistStatus === "success") {
      setArtist(data);
    }
  }, [artistStatus, data]); // 添加data依赖,确保获取最新数据

  return (
    <section className={styles.wrapper}>
      {/* 确保artist存在后再渲染内容,避免undefined报错 */}
      {artist && (
        <>
          <Head>
            <title>{artist.screenName}</title>
          </Head>
          <div className={styles.artistPage}>
            <ArtistHeader artist={artist} />
            <div className={styles.songContainer}>
              <UploadButton />
            </div>
          </div>
        </>
      )}
    </section>
  );
}

修改后的fetchArtist函数代码

import { getDoc, doc } from "firebase/firestore";
import { db } from "../pages/api/auth/firebase/config";

// 接收artistId作为参数,不再内部调用useRouter
export default async function fetchArtist(artistId) {
  if (!artistId) return null;
  
  const artistRef = doc(db, "users", artistId);
  const artistDoc = await getDoc(artistRef);
  
  if (artistDoc.exists()) {
    return artistDoc.data();
  } else {
    console.log("指定的艺术家文档不存在");
    return null;
  }
}

关键优化点

  1. 遵守Hook规则:将useRouter移到组件顶层调用,避免在普通函数内使用Hook
  2. 控制请求时机:通过enabled选项确保路由参数就绪后再发起请求,避免无效调用
  3. 缓存与更新:将artistId加入React Query的查询键,确保参数变化时自动重新请求,避免缓存旧数据
  4. 空值处理:添加artist存在性判断后再渲染内容,避免undefined引发的渲染错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:55:30