
本文详解如何在 next.js 13+ app router 中,通过服务端组件(server component)预获取 api 数据,并安全、高效地将结构化数据作为 props 传递给客户端组件(client component),避免“objects are not valid as a react child”等常见错误。
本文详解如何在 next.js 13+ app router 中,通过服务端组件(server component)预获取 api 数据,并安全、高效地将结构化数据作为 props 传递给客户端组件(client component),避免“objects are not valid as a react child”等常见错误。
在 Next.js 的 App Router 架构中,服务端组件(Server Components)默认支持异步数据获取,而客户端组件(Client Components)则负责交互逻辑与状态管理。一个典型需求是:在首屏加载时复用已缓存的 API 数据(如 CMS 内容),而非每次在浏览器端重复请求 。实现这一目标的关键在于—— 让数据获取发生在服务端,并以纯 JSON 可序列化的 props 形式向下透传,而非尝试“渲染服务端组件作为子元素”或返回非 JSX 对象。
你遇到的报错:
Error: Objects are not valid as a React child (found: object with keys {props})
根本原因在于 ServerComponent.js 中错误地返回了一个普通 JavaScript 对象 {props: { data} },而 React 期望组件函数返回的是合法的 JSX 元素(如 <div>、<Fragment> 等),而非裸对象。服务端组件 不能也不应返回 props 对象 ;它应当直接渲染 UI,或——更常见地—— 被省略,由父级服务端组件统一获取数据并注入子组件。
✅ 正确做法:将数据获取逻辑上提至页面根组件(page.js),利用其 async 特性预取数据,并以标准 props 方式传入客户端组件。这是 Next.js 官方推荐的数据流模式,兼顾性能、缓存控制与类型安全。
以下是优化后的完整实现:
app/page.js(服务端组件,自动启用数据缓存)
// app/page.js import Home from './clientComponent'; // ✅ 页面组件必须声明为 async 才能使用 await export default async function Page() { const data = await getData(); // ✅ 直接将解析后的 JSON 数据作为 props 传入客户端组件 return <Home data={data} />; } // 数据获取函数(可提取到 utils/api.js)async function getData() { const res = await fetch('https://api.example.com/pages/1', { next: { revalidate: 3600}, // ⚠️ 启用 ISR 缓存:每小时重新验证 }); if (!res.ok) {throw new Error(`Failed to fetch page data: ${res.status}`); } return res.json(); // ✅ 返回 plain object,可安全序列化为 props}
app/clientComponent.js(客户端组件)
'use client'; // ✅ 必须显式声明 import {Fragment} from 'react'; import Image from 'next/image'; import Typography from '@mui/material/Typography'; import styles from './page.module.css'; export default function Home({data}) {// ✅ 数据已在服务端获取并序列化,此处可直接消费 const set1 = data.textblockset?.find(item => item.id === 1); if (!set1 || !Array.isArray(set1.textblock)) {return <main className={styles.main}> 加载中或内容为空 </main>; } return (<main className={styles.main}> {set1.textblock.map((item) => (<Fragment key={item.id}> {item.block_icon && ( <Image src={item.block_icon} alt="icon" width={50} height={50} loading="lazy" className={styles.icon} /> )} <Typography paragraph fontWeight="bold"> {item.block_title} </Typography> <Typography>{item.block_content}</Typography> </Fragment> ))} </main> ); }
? 关键注意事项与最佳实践:
- 不要在服务端组件中返回非 JSX 对象:return {props: { …} } 是无效模式,React 不会将其解释为 props,而是尝试渲染该对象 → 触发报错。
- page.js 天然是服务端组件:无需额外包装或嵌套,直接 async + await 即可,且自动继承 Next.js 的数据缓存策略(如 fetch(…, { next: { revalidate} }))。
- 客户端组件接收的 props 必须可序列化:确保 data 是纯 JSON 类型(object/array/string/number/boolean/null),不含 Date、Map、Function 或自定义类实例。若需转换,应在服务端完成(例如 new Date().toISOString())。
- 错误边界建议:在客户端组件中添加空值校验(如 ?. 和 Array.isArray()),避免因服务端数据结构变动导致渲染崩溃。
- 性能提示:若 Home 组件实际无需交互(如仅展示静态内容),可考虑改为服务端组件,进一步减少客户端 bundle 体积。
通过这种清晰分层的数据流——服务端获取 → 序列化 → 客户端消费——你既能享受服务端缓存带来的性能优势,又能保留客户端组件所需的交互能力,真正实现「一次获取、多次复用」的现代前端架构目标。






























