Next.js/Sanity博客中如何将博文渲染为数组而非对象?
问题描述
我在Next.js/Sanity Studio搭建的博客中,尝试在page.tsx渲染测试博文时遇到如下错误:
Error: Objects are not valid as a React child (found: object with keys {_id, categories, title, _updatedAt, author, _createdAt, _rev, slug, _type}). If you meant to render a collection of children, use an array instead.
当Sanity Studio后台存在博文时触发该错误,无博文时则正常。我已尝试使用Object.values()、Array.isArray()等方法将响应转为数组,但错误仍存在。相关代码如下:
page.tsx
import { cookies } from 'next/headers' import PreviewSuspense from '../components/PreviewSuspense' import { DocumentsCount, query } from '../components/DocumentsCount' import PreviewDocumentsCount from '../components/PreviewDocumentsCount' import PreviewBlogList from '../components/PreviewBlogList' import { client } from '../../lib/client' import { cache } from 'react' import BlogList from '../components/BlogList' // Enable NextJS to cache and dedupe queries const clientFetch = cache(client.fetch.bind(client)) export default async function HomePage() { // Check if next preview data cookie is present if (cookies().get('__next_preview_data')) { return ( <PreviewSuspense fallback="Loading..."> <PreviewDocumentsCount /> <div>Preview Mode</div> <PreviewBlogList query={query} /> </PreviewSuspense> ) } const response = await clientFetch(query) const posts = Array.isArray(response) ? response : [] if (!posts || posts.length === 0) { return <div>No posts available.</div> } return ( <div> <DocumentsCount data={posts} /> <br /> Not in Preview Mode <BlogList posts={posts} /> </div> ) }
DocumentsCount.tsx
import groq from 'groq' export const query = groq` *[_type=='post'] { ..., author->, categories[]-> } | order(_createdAt desc) ` export function DocumentsCount({ data }: any) { return ( <> Documents: <strong>{data}</strong> </> ) }
PreviewDocumentsCount.tsx
'use client' import { usePreview } from '../../lib/preview' import { query, DocumentsCount } from '../components/DocumentsCount' export default function PreviewDocumentsCount() { const data = usePreview(null, query) return <DocumentsCount data={data} /> }
BlogList.tsx
type Props = { posts: Post[] } function BlogList({ posts }: Props) { return <div>BlogList</div> } export default BlogList
PreviewBlogList.tsx
'use client' import { usePreview } from '@/lib/preview' import BlogList from './BlogList' type Props = { query: string } export default function PreviewBlogList({ query }: Props) { const posts = usePreview(null, query) // console.log('Loading Posts...', posts) return <BlogList posts={posts} /> }
typings.d.ts
type Base = { _createdAt: string _id: string _rev: string _type: string _updatedAt: string } interface Post extends Base { author: Author body: Block[] categories: Category[] mainImage: Image slug: Slug title: string description: string } interface Author extends Base { bio: Block[] image: Image name: string slug: Slug } interface Image { _type: "image" asset: Reference } interface Reference { _ref: string _type: "reference" } interface Slug { _type: "slug" current: string } interface Block { _key: string _type: "block" children: Span[] markDefs: any[] style: "normal" | "h1" | "h2" | "h3" | "h4" | "blockquote" } interface Span { _key: string _type: "span" marks: string[] text: string } interface Category extends Base { description: string title: string } interface MainImage { _type: "image" current: string } interface Title { _type: "string" current: string }
解决方案
问题根源
错误出在DocumentsCount组件:你直接将包含多个Post对象的数组作为React子元素渲染,而React无法直接渲染对象或数组,必须转换为可渲染的类型(如字符串、JSX元素)。
修复代码
修改DocumentsCount.tsx,将直接渲染data改为渲染数组长度(这应该是你需要的文档数量统计),同时补上正确的类型定义:
import groq from 'groq' export const query = groq` *[_type=='post'] { ..., author->, categories[]-> } | order(_createdAt desc) ` // 使用正确的类型代替any export function DocumentsCount({ data }: { data: Post[] }) { return ( <> Documents: <strong>{data.length}</strong> </> ) }
额外注意事项
- 后续开发
BlogList组件时,遍历posts数组渲染单篇博文时,要确保渲染的是Post对象的具体属性(如post.title、post.author.name),不要直接渲染整个Post对象。 - 预览模式下的
PreviewDocumentsCount会自动继承这个修复,因为它依赖DocumentsCount组件。
为什么数组转换没用?
你在page.tsx中将response转为数组的操作是正确的,但错误的核心不是数组本身,而是你试图直接把包含对象的数组丢给React渲染,这才是触发错误的原因。
内容的提问来源于stack exchange,提问作者solidwake
相关产品推荐
相关产品推荐

