Reputation: 2728
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 }
}
Upvotes: 2
Views: 6753
Reputation: 45883
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 bygetStaticProps
.
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.
Upvotes: 2