英文:
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 'useState')
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 "../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
hook code:
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("could not fetch the data")
}
)
// update state
setDocuments(results)
setIsLoading(false)
setError(null)
})
return () => 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 "../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>Loading...</p>;
}
if (error) {
return <p>Error happend</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;
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论