You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
        </>
    )
}

额外注意事项

  1. 后续开发BlogList组件时,遍历posts数组渲染单篇博文时,要确保渲染的是Post对象的具体属性(如post.title、post.author.name),不要直接渲染整个Post对象。
  2. 预览模式下的PreviewDocumentsCount会自动继承这个修复,因为它依赖DocumentsCount组件。

为什么数组转换没用?

你在page.tsx中将response转为数组的操作是正确的,但错误的核心不是数组本身,而是你试图直接把包含对象的数组丢给React渲染,这才是触发错误的原因。

内容的提问来源于stack exchange,提问作者solidwake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 05:32:50