我正在尝试将我的 React 简单电子商务应用程序转换为 NextJS,但在弄清楚如何在使用 Context 时预渲染我的产品数据时遇到问题。问题是,我不需要像基于订单的产品数量那样的动态更新系统,因为这只是一个演示应用程序,所以我应该能够预渲染所有内容并使用 getStaticProps 和 getStaticPaths,对吧?但我遇到了以下错误:
Error occurred prerendering page ___.
(每页)
TypeError: Cannot read properties of null (reading 'useContext')
(或 useState)
这个应用程序的工作方式是我接收十几个产品的 Firestore 数据库集合,然后将它们设置为 ProductContext 中的“产品”状态,然后我访问 ProductList 页面中的上下文以将数据映射到产品项目组件。由于 item 中的任何更新都应该只通过上下文状态进行,所以这应该只是客户端,对吗? cart、addToCart、checkout 等仅使用上下文中的状态。我也尝试过使用 getServerSideProps()。此外,UserContext NextJS 应该只在构建时访问 ProductContext 中的 Firestore 来获取产品,然后能够将它们映射出来,甚至在 ProductDetails 组件上使用 getStaticPaths 来为每个项目使用动态路径 (/pages/products/[id]) 。但它在预渲染这些页面中的每一个时都存在问题。
Error occurred prerendering page "/products/1"
文件结构:
-.next
-.vscode
-components
-context
-ProductContext
-firebase
-firebase.config.js
-CartItem.js
-Navbar.js
-ProductItem.js
-node_modules
-pages
-products
-[id].js
-_app.js
-_document.js
-about.js
-cart.js
-index.js
-public
-styles
-styles.css
etc.
_app.js:
export default function App({ Component, pageProps }) {
return (
<div>
<ProductProvider>
<Component {...pageProps} />
</ProductProvider>
</div>
)
}
ProductContext.js
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const paintingsRef = collection(db, 'paintings')
let paintings = []
onSnapshot(paintingsRef, (snapshot) => {
snapshot.docs.forEach((doc) => {
paintings.push({ ...doc.data(), id: doc.id })
})
console.log(paintings)
return paintings
})
const paintingsData = paintings
const ProductContext = createContext()
export function ProductProvider({ children }) {
const [cart, setCart] = useState([])
const [products, setProducts] = useState()
const [total, setTotal] = useState(0)
const [numberOfItems, setNumberOfItems] = useState(0)
setProducts(paintingsData)
... (eCommerce functions)
return (
<ProductContext.Provider
value={{
cart: cart,
setProducts: setProducts,
total: total,
numberOfItems: numberOfItems,
addToCart: addToCart,
removeFromCart: removeFromCart,
checkout: checkout,
}}
>
{children}
</ProductContext.Provider>
)
}
export function useProductContext() {
return useContext(ProductContext)
}
index.js(产品列表)
export default function ProductList() {
const { addToCart, products } = useProductContext()
return(
<>
<div className="App">
products.map to <ProductItem /> component etc HTML
</div>
</>
)
}
/pages/products/[id] 用于动态路由:
export default function ProductDetails({ products, addToCart }) {
const router = useRouter()
const { id } = router.query
return(
<div>
{
products.filter((product) => product.id === id)
.map((product, index) => (
etc, HTML
}
</div>
)}
export async function getStaticProps() {
const { products, addToCart } = useProductContext()
return {
props: {
products: products,
addToCart: addToCart
}
};
}
export async function getStaticPaths() {
return {
paths: [
{ params: { id: '1'} },
{ params: { id: '2'} },
{ params: { id: '3'} },
{ params: { id: '4'} },
{ params: { id: '5'} },
{ params: { id: '6'} },
{ params: { id: '7'} },
{ params: { id: '8'} },
{ params: { id: '9'} },
{ params: { id: '10'} },
{ params: { id: '11'} },
{ params: { id: '12'} },
],
fallback: true,
}
}
感谢任何帮助,如果我可以提供更多信息,请告诉我。
您无法在服务器中使用 React Context,因为此功能仅在客户端上可用。
为了解决您的问题,您应该执行以下操作:
在 getStaticProps 中从数据库获取所需数据。
将数据传递到您的页面组件。
使用从服务器传递的道具设置上下文值。
除此之外,你的代码还有一些问题。
您正在使用 getStaticProps ,它主要在页面内部用于在构建时获取数据,但您正在使用 onSnapshot 方法来监听文档的更改。我认为最好使用 getServerSideProps 来代替。
您正在上下文组件之外编写 onSnapshot 逻辑。因此,每次数据库上的数据更新时,只有您的 paintingsData 会更新,但您的上下文值不会更新,因为您的组件不会重新渲染。
希望它有帮助:
ProductContext.js
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const ProductContext = createContext();
export function ProductProvider({ children }) {
const [cart, setCart] = useState([])
const [products, setProducts] = useState()
const [total, setTotal] = useState(0)
const [numberOfItems, setNumberOfItems] = useState(0)
useEffect(() => {
const paintingsRef = collection(db, 'paintings')
onSnapshot(paintingsRef, (snapshot) => {
const paintings = [];
snapshot.docs.forEach((doc) => {
paintings.push({ ...doc.data(), id: doc.id })
});
setProducts(paintings)
return paintings
});
}, []);
... (eCommerce functions)
return (
<ProductContext.Provider
value={{
cart: cart,
setProducts: setProducts,
total: total,
numberOfItems: numberOfItems,
addToCart: addToCart,
removeFromCart: removeFromCart,
checkout: checkout,
}}
>
{children}
</ProductContext.Provider>
)
}
export function useProductContext() {
return useContext(ProductContext)
}
/pages/products/[id] 用于动态路由(使用 getStaticProps,因为你不能将 getStaticPaths 与 getServerSideProps 一起使用)
export default function ProductDetails({ products }) {
const { setProducts } = useProductContext()
const router = useRouter()
const { id } = router.query
useEffect(() => {
setProducts(products);
}, [products]);
return(
<div>
{
products.filter((product) => product.id === id)
.map((product, index) => (
etc, HTML
}
</div>
)}
export async function getStaticProps() {
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const paintingsRef = collection(db, 'paintings');
const snapshot = await getDocs(paintingsRef);
const paintings = [];
snapshot.docs.forEach((doc) => {
paintings.push({ ...doc.data(), id: doc.id })
});
return {
props: {
products: paintings
}
};
}
export async function getStaticPaths() {
return {
paths: [
{ params: { id: '1'} },
{ params: { id: '2'} },
{ params: { id: '3'} },
{ params: { id: '4'} },
{ params: { id: '5'} },
{ params: { id: '6'} },
{ params: { id: '7'} },
{ params: { id: '8'} },
{ params: { id: '9'} },
{ params: { id: '10'} },
{ params: { id: '11'} },
{ params: { id: '12'} },
],
fallback: true,
}
}
将 onSnapshot 更改为 getDocs 解决了 Firestore 数据获取问题!:
export async function getServerSideProps() {
const paintingsRef = collection(db, 'paintings')
const paintings = []
const snapshot = await getDocs(paintingsRef)
snapshot.forEach((doc) => {
paintings.push({ ...doc.data(), id: doc.id })
})
console.log(paintings)
return {
props: {
paintings: paintings,
}
}
}
嘿我也面临同样的问题你能帮我吗 导出在以下路径上遇到错误: /(主)/机构/页面:/机构 /(主)/子帐户/页面:/子帐户 /_错误:/404 /_错误:/500 /_未找到 /站点/页面:/站点 预渲染页面“/site”时发生错误。了解更多:https://nextjs.org/docs/messages/prerender-error 类型错误:无法读取 null 的属性(读取“useContext”)