沒有非同步客戶端元件
客戶端元件不能是非同步函式。
為何發生此錯誤
當您嘗試將客戶端元件定義為非同步函式時,就會發生此錯誤。React 客戶端元件不支援非同步函式。例如
'use client'
// This will cause an error
async function ClientComponent() {
// ...
}
可能的修正方式
- 轉換為伺服器元件:如果可以,請將您的客戶端元件轉換為伺服器元件。這可讓您直接在元件中使用
async
/await
。 - 移除
async
關鍵字:如果您需要將其保留為客戶端元件,請移除async
關鍵字並以不同的方式處理資料擷取。 - 使用 React Hooks 進行資料擷取:使用像
useEffect
這樣的 Hook 進行客戶端資料擷取,或使用第三方函式庫。 - 利用帶有 Context Provider 的
use
Hook:使用 Context 將 Promise 傳遞給子元件,然後使用use
Hook 解析它們。
建議:伺服器端資料擷取
我們建議在伺服器上擷取資料。例如
app/page.tsx
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
搭配 Context Provider 使用 use
另一種值得探索的模式是搭配 Context Provider 使用 React use
Hook。這可讓您將 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() {
const 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() {
const 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() {
const blogPromise = useBlogContext()
const 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() {
const [posts, setPosts] = useState(null)
useEffect(() => {
async function fetchPosts() {
const res = await fetch('https://api.vercel.app/blog')
const 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>
)
}
這有幫助嗎?