Next.js中服务端组件向客户端组件传递API缓存数据时遇React子元素错误的解决方案咨询
Next.js中服务端组件向客户端组件传递API缓存数据时遇React子元素错误的解决方案咨询
我来帮你分析并解决这个问题~你遇到的错误本质是对Next.js服务端组件的返回值要求理解有误,咱们一步步梳理清楚:
问题根源
你现在的ServerComponent.js返回了一个{props: {data}}对象,这是Next.js旧版(比如getServerSideProps)的页面数据返回格式,但服务端组件本身是标准React组件,必须返回JSX元素或null,不能直接返回这种props对象。React尝试渲染这个普通对象时,就会抛出「Objects are not valid as a React child」的错误。
而且你的嵌套逻辑也有点绕——客户端组件作为父组件包裹服务端组件的方式,没法直接拿到服务端组件的数据,正确的思路应该是让服务端组件获取数据后,把数据传递给客户端组件使用。
解决方案
下面给你两种符合Next.js 13+ App Router规范的实现方式,都能实现「服务端缓存API数据,传给客户端组件渲染」的需求:
方式一:服务端组件作为父组件传递数据(推荐)
这种方式逻辑最清晰,让服务端组件负责数据获取,再把数据作为props传给客户端组件:
- 修改
ServerComponent.js:获取数据后直接渲染客户端组件并传递数据
import { API } from "../utils/api"; import Home from './clientComponent'; export default async function Server() { const data = await getData(); // 服务端组件直接渲染客户端组件,把data作为props传入 return <Home data={data} />; } async function getData() { // fetch在Next.js服务端组件默认是force-cache,自动缓存数据 // 可通过next选项调整缓存策略,比如缓存一天 const res = await fetch(API.pages.retrieve(1), { next: { revalidate: 86400 } }); if (!res.ok) { throw new Error('Failed to fetch data'); } return res.json(); }
- 简化
page.js:直接引入服务端组件即可(page.js默认是服务端组件)
// page.js import Server from './ServerComponent' export default function Page() { return <Server />; }
- 修改
clientComponent.js:接收data props并渲染内容
'use client' import Image from 'next/image' import styles from './page.module.css' import Typography from '@mui/material/Typography'; export default function Home({ data }) { // 处理数据,注意加可选链避免空值报错 const set1 = data?.textblockset?.find(item => item.id === 1); return ( <main className={styles.main}> {set1?.textblock?.map((item, index) => ( <div key={index}> {item.block_icon && ( <Image src={item.block_icon} alt="icon" width={50} height={50} loading="lazy" /> )} <Typography paragraph fontWeight="bold"> {item.block_title} </Typography><br/> <Typography> {item.block_content} </Typography> </div> ))} </main> ) }
方式二:保持嵌套结构,服务端组件返回带数据的JSX
如果你想保留「客户端组件包裹服务端组件」的结构,可以让服务端组件返回包含数据的JSX元素,比如自定义一个数据容器:
- 修改
ServerComponent.js:
import { API } from "../utils/api"; export default async function Server() { const data = await getData(); // 返回一个包含数据的自定义元素,客户端组件可以通过children.props拿到数据 return <DataContainer data={data} />; } // 定义一个简单的容器组件 function DataContainer({ data }) { return <>{data}</>; } async function getData() { const res = await fetch(API.pages.retrieve(1), { next: { revalidate: 86400 } }); if (!res.ok) { throw new Error('Failed to fetch data'); } return res.json(); }
- 修改
clientComponent.js:从children中提取数据
'use client' import Image from 'next/image' import styles from './page.module.css' import Typography from '@mui/material/Typography'; export default function Home({ children }) { const data = children.props.data; const set1 = data?.textblockset?.find(item => item.id === 1); return ( <main className={styles.main}> {set1?.textblock?.map((item, index) => ( <div key={index}> {item.block_icon && ( <Image src={item.block_icon} alt="icon" width={50} height={50} loading="lazy" /> )} <Typography paragraph fontWeight="bold"> {item.block_title} </Typography><br/> <Typography> {item.block_content} </Typography> </div> ))} </main> ) }
关键提示
- 关于缓存:
fetch在Next.js服务端组件中默认使用force-cache策略,数据会被自动缓存,不会每次客户端访问都调用API。你可以通过next.revalidate设置缓存有效期,或者next.tags实现增量更新。 - 空值处理:渲染数据时记得加可选链(
?.),避免数据未加载或格式异常时抛出错误。
备注:内容来源于stack exchange,提问作者Matthias
相关产品推荐
相关产品推荐

