Next.js 13 + Sanity组件调用报错:Cannot read properties of undefined (reading 'map')
解决Next.js中组件内getStaticProps失效导致的TypeError问题
问题概述
- 背景:使用Sanity进行内容管理,在React项目中通过
useState+useEffect执行GROQ查询可正常运行;迁移至Next.js 13后,发现getStaticProps仅在pages目录下的页面组件中生效。 - 问题现象:将查询逻辑封装到
AboutCheck组件(存放于component目录),并在pages/index.jsx中引入该组件时,触发错误:TypeError: Cannot read properties of undefined (reading 'map')。 - 前置确认:GROQ查询本身可正常执行,仅在组件调用时出现错误。
核心原因
Next.js的getStaticProps(包括getServerSideProps、getStaticPaths)仅对pages目录下的页面组件生效,components目录下的组件中定义这些数据获取函数不会被Next.js框架执行。因此当在index.jsx中引入AboutCheck组件时,HeroSection props未被赋值,导致其为undefined,调用map方法时触发错误。
解决方案
方案一:将数据获取移至页面组件,通过props传递数据
将getStaticProps移到pages/index.jsx中执行数据查询,再将结果通过props传递给AboutCheck组件。
修改pages/index.jsx:
import Head from 'next/head' import Image from 'next/image' import { Inter } from 'next/font/google' import styles from '@/styles/Home.module.css' import AboutCheck from '@/component/AboutCheck' import groq from "groq"; import createClient from "../client"; const inter = Inter({ subsets: ['latin'] }) function Home({ HeroSection }) { return ( <> <Head> <title>Create Next App</title> <meta name="description" content="Generated by create next app" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.ico" /> </Head> <p>Hey</p> <AboutCheck HeroSection={HeroSection} /> </> ) } export async function getStaticProps() { const query = `*[_type == "HeroSection"]{ heroheading, herosubheading, ctaButtonText, coverImage { asset -> { url } } }`; const HeroSection = await createClient.fetch(query); return { props: { HeroSection, }, }; } export default Home;
修改AboutCheck组件(仅保留UI渲染逻辑,增加空值判断):
const AboutCheck = ({ HeroSection }) => { // 空值判断避免数据未加载时出错 if (!HeroSection) return <div>Loading...</div>; return ( <div> {HeroSection.map((item) => ( <li key={item._id}>{item.heroheading}</li> ))} </div> ); }; export default AboutCheck;
方案二:组件内客户端渲染获取数据
如果需要组件独立获取数据,可在组件内通过useEffect执行客户端查询,适合无需静态生成的场景:
import { useState, useEffect } from 'react'; import groq from "groq"; import createClient from "../client"; const AboutCheck = () => { const [HeroSection, setHeroSection] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { const query = `*[_type == "HeroSection"]{ heroheading, herosubheading, ctaButtonText, coverImage { asset -> { url } } }`; const data = await createClient.fetch(query); setHeroSection(data); setLoading(false); }; fetchData(); }, []); if (loading) return <div>Loading...</div>; return ( <div> {HeroSection.map((item) => ( <li key={item._id}>{item.heroheading}</li> ))} </div> ); }; export default AboutCheck;
方案三:使用Next.js 13 App Router的Server Component
如果项目采用Next.js 13+推荐的App Router(app目录),可将AboutCheck设为Server Component,直接在组件内编写异步数据获取逻辑:
// app/components/AboutCheck.jsx import groq from "groq"; import createClient from "../../client"; async function AboutCheck() { const query = `*[_type == "HeroSection"]{ heroheading, herosubheading, ctaButtonText, coverImage { asset -> { url } } }`; const HeroSection = await createClient.fetch(query); return ( <div> {HeroSection.map((item) => ( <li key={item._id}>{item.heroheading}</li> ))} </div> ); } export default AboutCheck;
在app/page.jsx中直接引入该组件即可,无需额外传递props。
内容的提问来源于stack exchange,提问作者Anurag
相关产品推荐
相关产品推荐

