TypeError: 在 Next.js 中无法读取 null 的属性(读取 ‘useState’)。

huangapple go评论70阅读模式
英文:

TypeError: Cannot read properties of null (reading 'useState') in Next.js

问题

我在使用我的自定义钩子时遇到了这个错误消息:TypeError: Cannot read properties of null (reading 'useState'),当我尝试在getStaticProps中使用它来从Firebase Firestore获取数据时。有人能帮帮我吗?

Challenges 页面的代码:

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

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 { documents, isLoading } = useCollection("challenges", null, null, [
    "createdAt",
    "desc",
  ])

  return {
    props: {
      challenges: documents,
    },
  }
}

useCollection 钩子的代码:

import { useEffect, useState } from "react"
import { collection, limit, onSnapshot, orderBy, query, where } from "firebase/firestore"
import { db } from "../firebase/config"

export const useCollection = (c, q, l, o) => {
  const [documents, setDocuments] = useState([])
  const [isLoading, setIsLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    let ref = collection(db, c)
    if (q) {
      ref = query(ref, where(...q))
    }
    if (o) {
      ref = query(ref, orderBy(...o))
    }
    if (l) {
      ref = query(ref, limit(l))
    }

    const unsubscribe = onSnapshot(ref, (snapshot) => {
      const results = []

      snapshot.docs.forEach(
        (doc) => {
          results.push({ ...doc.data(), id: doc.id })
        },
        (error) => {
          console.log(error)
          setError("无法获取数据")
        }
      )
      // 更新状态
      setDocuments(results)
      setIsLoading(false)
      setError(null)
    })

    return () => unsubscribe()
  }, [])

  return { documents, error, isLoading }
}
英文:

I'm getting this error message TypeError: Cannot read properties of null (reading &#39;useState&#39;) when I use my custom hooks inside the getStaticProps to fetch the data from the firebase firestore. Anyone, please help me with this?

Challenges page code:

import Card from &quot;../components/reusable/Card&quot;
import { useCollection } from &quot;../hooks/useCollection&quot;

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

export default Challenges

export async function getStaticProps() {
  const { documents, isLoading } = useCollection(&quot;challenges&quot;, null, null, [
    &quot;createdAt&quot;,
    &quot;desc&quot;,
  ])

  return {
    props: {
      challenges: documents,
    },
  }
}

useCollection hook code:

import { useEffect, useState } from &quot;react&quot;
import { collection, limit, onSnapshot, orderBy, query, where } from &quot;firebase/firestore&quot;

import { db } from &quot;../firebase/config&quot;

export const useCollection = (c, q, l, o) =&gt; {
  const [documents, setDocuments] = useState([])
  const [isLoading, setIsLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() =&gt; {
    let ref = collection(db, c)
    if (q) {
      ref = query(ref, where(...q))
    }
    if (o) {
      ref = query(ref, orderBy(...o))
    }
    if (l) {
      ref = query(ref, limit(l))
    }

    const unsubscribe = onSnapshot(ref, (snapshot) =&gt; {
      const results = []

      snapshot.docs.forEach(
        (doc) =&gt; {
          results.push({ ...doc.data(), id: doc.id })
        },
        (error) =&gt; {
          console.log(error)
          setError(&quot;could not fetch the data&quot;)
        }
      )
      // update state
      setDocuments(results)
      setIsLoading(false)
      setError(null)
    })

    return () =&gt; unsubscribe()
  }, [])

  return { documents, error, isLoading }
}

答案1

得分: 2

你可以仅在React组件内或自定义钩子内使用useState或任何钩子,如Hooks规则所述,而getStaticProps不属于其中任何一个。而且,它仅在构建时运行,因此不会发送到浏览器:

如果你从页面中导出一个名为getStaticProps(静态站点生成)的函数,Next.js将使用getStaticProps返回的props在构建时预渲染此页面。

你可以将数据获取移至客户端并且去掉getStaticProps,像这样:

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 <p>加载中...</p>;
  }
  if (error) {
    return <p>发生错误</p>;
  }
  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;

或者将useCollection转换为一个普通的异步函数,不包含任何钩子,然后在getStaticProps内使用它。但这似乎不是你想要的,因为你在客户端上创建了一个订阅。

英文:

You can use useState or any hook only inside a React Component, or a custom hook as Rules of Hooks says, and getStaticProps is none of the two. Plus, it only runs at build time, so not sent to the browser:
>If you export a function called getStaticProps (Static Site Generation) from a page, Next.js will pre-render this page at build time using the props returned by getStaticProps.

You could either move the data fetching to the client and get rid of getStaticPros, like so:

import Card from &quot;../components/reusable/Card&quot;;
import { useCollection } from &quot;../hooks/useCollection&quot;;

const Challenges = () =&gt; {
  const {
    documents: challenges,
    isLoading,
    error,
  } = useCollection(&quot;challenges&quot;, null, null, [&quot;createdAt&quot;, &quot;desc&quot;]);

  if (isLoading) {
    return &lt;p&gt;Loading...&lt;/p&gt;;
  }
  if (error) {
    return &lt;p&gt;Error happend&lt;/p&gt;;
  }
  return (
    &lt;div className=&quot;grid sm:grid-cols-2 lg:grid-cols-3 gap-6 justify-items-center mt-8&quot;&gt;
      {challenges.map((challenge) =&gt; {
        return &lt;Card key={challenge.id} card={challenge} /&gt;;
      })}
    &lt;/div&gt;
  );
};

export default Challenges;

Or transform useCollection to a normal async function without any hook in it, and use it inside getStaticProps. But it doesn't seem like it's what you want, since you are creating a subscription on the client and all.

huangapple
  • 本文由 发表于 2023年2月8日 18:31:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/75384453.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定