跳至内容
文档错误异步客户端组件

异步客户端组件

客户端组件不能是异步函数。

此错误发生的原因

当您尝试将客户端组件定义为异步函数时,会发生此错误。React 客户端组件不支持异步函数。例如

'use client'
 
// This will cause an error
async function ClientComponent() {
  // ...
}

可能的解决方法

  1. 转换为服务器组件:如果可能,将您的客户端组件转换为服务器组件。这允许您直接在组件中使用async/await
  2. 删除async关键字:如果您需要将其保留为客户端组件,请删除async关键字并以不同的方式处理数据获取。
  3. 使用 React Hook 进行数据获取:利用useEffect等 Hook 进行客户端数据获取,或使用第三方库。
  4. 利用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 库(例如SWRReact 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>
  )
}