Next.js 13:客户端组件导入异步服务端组件报错原因解析
为什么异步Server Component作为Client Component子元素会报错?
我在测试Next.js 13的app目录和React 18的Server Components功能。按照文档指引,我给Server Component添加了async fetch()调用并将组件标记为async,但把它作为Client Component的子元素时,出现了以下错误:
Objects are not valid as a React child (found: [object Promise])
如果把它作为另一个Server Component的子元素则能正常运行,请问这是为什么?
代码示例
page.tsx
import { ClientComp } from './ClientComp' export default function Page() { return <ClientComp /> }
ClientComp.tsx
"use client"; import { ServerComp } from "./ServerComp"; export function ClientComp() { {/* @ts-expect-error Async Server Component */} return <ServerComp /> }
ServerComp.tsx
export async function ServerComp() { return <h2>ServerComp</h2> }
原因解析
这是因为Client Component无法直接渲染异步的Server Component:
- Server Component运行在服务端,异步组件返回的Promise会被服务端处理,等待其解析为实际React元素后再发送给客户端,所以作为其他Server Component的子元素时能正常工作。
- Client Component运行在浏览器端,当你在Client Component中直接调用
<ServerComp />,React会把这个异步组件的返回值(Promise)当作普通React子元素处理,但React不允许将Promise作为子元素渲染,因此抛出错误。
解决方法
方法1:将Server Component的渲染结果通过props传递给Client Component
把异步Server Component的渲染逻辑提升到父级Server Component中,等待解析完成后再将结果传给Client Component:
修改page.tsx:
import { ClientComp } from './ClientComp' import { ServerComp } from './ServerComp' export default async function Page() { const serverContent = await ServerComp() return <ClientComp serverContent={serverContent} /> }
修改ClientComp.tsx:
"use client"; export function ClientComp({ serverContent }: { serverContent: React.ReactNode }) { return serverContent }
方法2:用Suspense包裹Server Component(需在Server上下文引入)
如果需要在Client Component内部动态渲染Server Component,可以在父级Server Component中用Suspense包裹后传给Client,或者通过动态导入标记为server组件:
// 在Server Component中处理 import { Suspense } from 'react' import { ClientComp } from './ClientComp' import { ServerComp } from './ServerComp' export default function Page() { return ( <ClientComp> <Suspense fallback={<div>加载中...</div>}> <ServerComp /> </Suspense> </ClientComp> ) }
此时Client Component只需接收子元素并渲染:
"use client"; export function ClientComp({ children }: { children: React.ReactNode }) { return <div>{children}</div> }
内容的提问来源于stack exchange,提问作者Cla
相关产品推荐
相关产品推荐

