异步客户端组件
客户端组件不能是异步函数。
此错误发生的原因
当您尝试将客户端组件定义为异步函数时,会发生此错误。React 客户端组件不支持异步函数。例如
'use client'
// This will cause an error
async function ClientComponent() {
// ...
}
可能的解决方法
- 转换为服务器组件:如果可能,将您的客户端组件转换为服务器组件。这允许您直接在组件中使用
async
/await
。 - 删除
async
关键字:如果您需要将其保留为客户端组件,请删除async
关键字并以不同的方式处理数据获取。 - 使用 React Hook 进行数据获取:利用
useEffect
等 Hook 进行客户端数据获取,或使用第三方库。 - 利用
use
Hook 与 Context Provider:使用 Context 将 Promise 传递给子组件,然后使用use
Hook 解析它们。
推荐:服务器端数据获取
我们建议在服务器上获取数据。例如
app/page.tsx
export default async function Page() {
let data = await fetch('https://api.vercel.app/blog')
let posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
使用use
与 Context Provider
另一个需要探索的模式是使用 React 的use
Hook 与 Context Provider。这允许您将 Promise 传递给子组件,并使用use
Hook 解析它们。这是一个示例
首先,让我们为 Context Provider 创建一个单独的文件
app/context.tsx
'use client'
import { createContext, useContext } from 'react'
export const BlogContext = createContext<Promise<any> | null>(null)
export function BlogProvider({
children,
blogPromise,
}: {
children: React.ReactNode
blogPromise: Promise<any>
}) {
return (
<BlogContext.Provider value={blogPromise}>{children}</BlogContext.Provider>
)
}
export function useBlogContext() {
let context = useContext(BlogContext)
if (!context) {
throw new Error('useBlogContext must be used within a BlogProvider')
}
return context
}
现在,让我们在服务器组件中创建 Promise 并将其流式传输到客户端
app/page.tsx
import { BlogProvider } from './context'
export default function Page() {
let blogPromise = fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return (
<BlogProvider blogPromise={blogPromise}>
<BlogPosts />
</BlogProvider>
)
}
这是博客文章组件
app/blog-posts.tsx
'use client'
import { use } from 'react'
import { useBlogContext } from './context'
export function BlogPosts() {
let blogPromise = useBlogContext()
let posts = use(blogPromise)
return <div>{posts.length} blog posts</div>
}
此模式允许您尽早开始数据获取并将 Promise 传递给子组件,然后子组件可以使用use
Hook 在数据准备就绪时访问数据。
客户端数据获取
在需要客户端获取的场景中,您可以在useEffect
中调用fetch
(不推荐),或者依靠社区中流行的 React 库(例如SWR或React Query)进行客户端获取。
app/page.tsx
'use client'
import { useState, useEffect } from 'react'
export function Posts() {
let [posts, setPosts] = useState(null)
useEffect(() => {
async function fetchPosts() {
let res = await fetch('https://api.vercel.app/blog')
let data = await res.json()
setPosts(data)
}
fetchPosts()
}, [])
if (!posts) return <div>Loading...</div>
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
这有帮助吗?