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

Next.js中getStaticProps调用自定义Hook出现useState读取错误求助

问题原因

你遇到的TypeError: Cannot read properties of null (reading 'useState')错误,核心原因是React Hook不能在非React组件/自定义Hook的函数中调用。

useCollection是自定义React Hook,内部依赖useState、useEffect这些Hook,而getStaticProps是Next.js提供的服务器端数据获取函数,它不属于React组件或自定义Hook范畴,完全脱离了React的Hook调用规则环境,直接调用Hook会导致React无法初始化对应的状态上下文,进而抛出这个错误。

另外,useCollection里用了onSnapshot(实时监听Firestore数据变化),但getStaticProps的作用是在构建时一次性获取数据用于预渲染页面,实时监听在这里完全没必要,反而会造成资源浪费。

解决方案

我们需要把Firestore数据获取逻辑从Hook中抽离出来,改成普通的异步函数,在getStaticProps中调用这个函数来获取数据:

步骤1:创建数据获取工具函数

新建一个工具文件(比如utils/firebaseUtils.js),写一个异步函数来获取挑战数据:

import { collection, query, orderBy, getDocs } from "firebase/firestore"
import { db } from "../firebase/config"

export async function getChallenges() {
  // 构建查询条件,和原Hook逻辑一致
  const challengesRef = collection(db, "challenges")
  const q = query(challengesRef, orderBy("createdAt", "desc"))
  
  // 一次性获取数据(替代实时监听的onSnapshot)
  const snapshot = await getDocs(q)
  
  // 转换数据格式
  return snapshot.docs.map(doc => ({ ...doc.data(), id: doc.id }))
}

步骤2:修改Challenges页面的getStaticProps

删除对useCollection的导入,改用上面的工具函数:

import Card from "../components/reusable/Card"
import { getChallenges } from "../utils/firebaseUtils"

const Challenges = ({ challenges }) => {
  return (
        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6 justify-items-center mt-8">
          {challenges.map((challenge) => {
                return (
                  <Card key={challenge.id} card={challenge} />
                )
              })}
        </div>
  )
}

export default Challenges

export async function getStaticProps() {
  // 调用工具函数获取数据
  const challenges = await getChallenges()

  return {
    props: {
      challenges,
    },
    // 可选:如果需要页面定期重新生成以更新数据,添加revalidate参数
    // revalidate: 60, // 每60秒重新获取数据并生成页面
  }
}

额外说明

如果你的页面需要实时更新Firestore数据,可以在Challenges组件内部调用useCollection Hook,但此时不需要getStaticProps(或者保留getStaticProps做预渲染,客户端再用Hook做实时更新):

import Card from "../components/reusable/Card"
import { useCollection } from "../hooks/useCollection"

const Challenges = () => {
  const { documents: challenges, isLoading, error } = useCollection("challenges", null, null, ["createdAt", "desc"])

  if (isLoading) return <div>加载中...</div>
  if (error) return <div>{error}</div>

  return (
        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6 justify-items-center mt-8">
          {challenges.map((challenge) => {
                return (
                  <Card key={challenge.id} card={challenge} />
                )
              })}
        </div>
  )
}

export default Challenges

// 此时不需要getStaticProps

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 01:13:59