Next.js 14.1 App Router报错:Server组件被误判为Client组件
我使用Next.js v14.1及App Router,在src/app/page.tsx中将PostsFeed作为ClientComponent的子组件传入,遵循官方组件组合模式,却收到错误提示:
'async/await is not yet supported in Client Components, only Server Components.'
我无法理解为何Next.js会把PostsFeed识别为客户端组件,相关代码如下:
// src/app/page.tsx export default function Home() { return ( <main className={styles.main}> ... .... .... <section> <ClientComponent> <PostsFeed /> </ClientComponent> </section> </main> ) }
// ClientComponent.tsx "use client"; export default function ClientComponent({ children, }: { children: React.ReactNode; }) { return <> {children} </>; }
// PostsFeed.tsx import { PostCard } from "./PostCard"; export const PostsFeed = async () => { const res = await fetch(`/posts`); const { data } = await res.json(); let posts = data as IPost[]; return ( <div className="grid grid-cols-3 gap-4"> {posts.map((post, index) => ( <PostCard key={index} post={post} /> ))} </div> ); };
问题原因
Next.js的核心规则是:客户端组件(带"use client"指令)的所有子组件都会被强制转为客户端组件。当你把PostsFeed作为ClientComponent的children传入时,它会被纳入客户端执行上下文,而客户端组件不支持async/await语法,因此触发报错。
解决方案
根据业务需求,可选择以下两种方式:
方式1:保持PostsFeed为服务器组件
将PostsFeed从ClientComponent的children中移出,直接在服务器组件(page.tsx)里异步渲染它,再把渲染结果作为内容传给ClientComponent。这样PostsFeed依然是服务器组件,能正常使用async/await。
修改后的page.tsx代码:
// src/app/page.tsx import { PostsFeed } from './PostsFeed'; import ClientComponent from './ClientComponent'; export default async function Home() { // 在服务器组件中异步渲染PostsFeed const postsFeedContent = await PostsFeed(); return ( <main className={styles.main}> ... <section> <ClientComponent> {postsFeedContent} </ClientComponent> </section> </main> ) }
PostsFeed.tsx和ClientComponent.tsx无需修改。
方式2:将PostsFeed改为客户端组件
如果PostsFeed需要在客户端运行(比如要使用useState、useEffect等客户端Hook),就给它加上"use client"指令,同时把async/await替换为客户端支持的数据获取方式,比如用useEffect实现异步请求。
修改后的PostsFeed.tsx代码:
// PostsFeed.tsx "use client"; import { useState, useEffect } from 'react'; import { PostCard } from "./PostCard"; export const PostsFeed = () => { const [posts, setPosts] = useState<IPost[]>([]); useEffect(() => { const fetchPosts = async () => { const res = await fetch(`/posts`); const { data } = await res.json(); setPosts(data as IPost[]); }; fetchPosts(); }, []); return ( <div className="grid grid-cols-3 gap-4"> {posts.map((post, index) => ( <PostCard key={index} post={post} /> ))} </div> ); };
内容的提问来源于stack exchange,提问作者probaku1234

