如何在服务端为Next.js Client Components获取数据
关于Next.js App Router中服务端数据传递给客户端组件的疑问
概念定义
- Client Components:顶部标注
'use client'的文件 - Server Components:顶部标注
'use server'的文件(注:App Router中默认组件为Server Component,无需显式标注'use server')
问题背景
我未找到相关资料说明如何在服务端获取数据后直接传递给Client Components,且Client Components中禁止使用async/await函数或组件,尝试预渲染会触发如下报错:
Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.
Next.js文档给出的客户端数据获取方式为通过useEffect发起请求:
useEffect(() => { fetch('/api/profile-data') .then((res) => res.json()) .then((data) => { setData(data) setLoading(false) }) }, [])
但该方案无法实现服务端获取数据后直接填充Client Components的需求,同时原有的getStaticProps/getServerSideProps在App Router中已不再可用。
尝试的解决方案
我通过拆分文件的方式实现需求,目录结构如下:
/app page.tsx clientPage.tsx layout.tsx
page.tsx(Server Component)
// App Router默认是Server Component,显式标注仅作说明 "use server" import React from 'react' import { ClientPage } from './clientPage' const getServerDataForClient = async () => { console.log('That was executed on the server side') const serverData = await new Promise(resolve => { setTimeout(() => resolve({ someData: 'secret' }), 3000) }) return serverData } const Page = async () => { console.log('That also was executed on the server side') const serverData = await getServerDataForClient() return <ClientPage serverData={serverData} /> } export default Page
clientPage.tsx(Client Component)
'use client' import React, { FC, useEffect, useState } from 'react' type TClientPageProps = { serverData: { someData: string } } // 首次渲染在服务端执行,会忽略浏览器专属逻辑(如useEffect、useLayoutEffect等) // 仅返回包含初始状态(如useState的初始值、useRef)的HTML export const ClientPage: FC<TClientPageProps> = ({ serverData }) => { // 服务端和浏览器首次渲染都会拿到这个初始值 const [state, setState] = useState('server state') console.log("This log will be shown in the server logs on component's first mount and browsers on all mounts") useEffect(() => { // 这段逻辑不会在服务端执行,仅在浏览器组件挂载后触发 setState('browser state') }, []) return ( <main> <div>State is: {state}</div> <div>Server data is: {serverData.someData}</div> </main> ) }
这种方式通过Server Component处理服务端数据逻辑,Client Component实现UI交互,无需依赖旧的getServerSideProps/getStaticProps,但必须拆分文件。
疑问点
- 上述用法是否正确?有没有遗漏的注意事项?
- 如果存在多层嵌套组件,是不是只能通过props drilling来传递服务端数据?这一点我比较困惑。
附加测试情况
- 若将
page.tsx与clientPage.tsx合并,会触发报错:"useState" is not allowed in Server Components. - 尝试在Client Components中用Server Action获取数据并预渲染,代码如下:
const getServerDataForClient = async () => { 'use server' console.log('That was executed on the server side') const serverData = await new Promise(resolve => { setTimeout(() => resolve({ someData: 'secret' }), 3000) }) return serverData }
会报错,因为Client Components无法用async/await等待Server Action的结果。
- 考虑过给所有页面组件添加
'use client',例如:
/src/components/Partners "use client" export const Partners: FC = () => // some code here
但这样会降低App Router的使用体验,同一页面包含多个此类组件时还需额外创建聚合组件。
内容的提问来源于stack exchange,提问作者Ahmed Faraz
相关产品推荐
相关产品推荐

