非同步用戶端元件錯誤
用戶端元件不能是非同步函式。
錯誤原因
當您嘗試將用戶端元件定義為非同步函式時,就會發生此錯誤。React 用戶端元件不支援非同步函式。例如:
'use client'
// This will cause an error
async function ClientComponent() {
// ...
}
可能的解決方法
- 轉換為伺服器元件:如果可以,請將您的客戶端元件轉換為伺服器元件。這讓您可以直接在元件中使用
async
/await
。 - 移除
async
關鍵字:如果您需要將其保留為客戶端元件,請移除async
關鍵字並以其他方式處理資料擷取。 - 使用 React hooks 進行資料擷取:利用
useEffect
等 hooks 進行客戶端資料擷取,或使用第三方函式庫。 - 使用 Context Provider 搭配
use
hook:使用 context 將 promises 傳遞給子元件,然後使用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>
)
}
使用 Context Provider 搭配 use
另一種可以探索的模式是使用 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>
)
}
這有幫助嗎?