mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
be56ce3ae8
### What? `use` is not a hook. It's highlighted in the doc > Unlike React Hooks, use can be called within loops and conditional statements like if. Like React Hooks, the function that calls use must be a Component or Hook. ### Why? It confuses consumers of Next.js and adds field provide misleading proofs ### How? Closes NEXT- Fixes # --> --------- Co-authored-by: Joseph <joseph.chamochumbi@vercel.com> Co-authored-by: Joseph <sephxd1234@gmail.com>
147 lines
4.0 KiB
Plaintext
147 lines
4.0 KiB
Plaintext
---
|
|
title: No async Client Component
|
|
---
|
|
|
|
> Client components cannot be async functions.
|
|
|
|
## Why This Error Occurred
|
|
|
|
The error occurs when you try to define a Client Component as an async function. React Client Components [do not support](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#why-cant-client-components-be-async-functions) async functions. For example:
|
|
|
|
```tsx
|
|
'use client'
|
|
|
|
// This will cause an error
|
|
async function ClientComponent() {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
## Possible Ways to Fix It
|
|
|
|
1. **Convert to a Server Component**: If possible, convert your Client Component to a Server Component. This allows you to use `async`/`await` directly in your component.
|
|
2. **Remove the `async` keyword**: If you need to keep it as a Client Component, remove the `async` keyword and handle data fetching differently.
|
|
3. **Use React Hooks for data fetching**: Utilize hooks like `useEffect` for client-side data fetching, or use third-party libraries.
|
|
4. **Leverage the `use` API with a Context Provider**: Pass promises to child components using context, then resolve them with the `use` API.
|
|
|
|
### Recommended: Server-side data fetching
|
|
|
|
We recommend fetching data on the server. For example:
|
|
|
|
```tsx filename="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>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Using `use` with Context Provider
|
|
|
|
Another pattern to explore is using the React `use` API with a Context Provider. This allows you to pass Promises to child components and resolve them using the `use` API . Here's an example:
|
|
|
|
First, let's create a separate file for the context provider:
|
|
|
|
```tsx filename="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
|
|
}
|
|
```
|
|
|
|
Now, let's create the Promise in a Server Component and stream it to the client:
|
|
|
|
```tsx filename="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>
|
|
)
|
|
}
|
|
```
|
|
|
|
Here is the blog posts component:
|
|
|
|
```tsx filename="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>
|
|
}
|
|
```
|
|
|
|
This pattern allows you to start data fetching early and pass the Promise down to child components, which can then use the `use` API to access the data when it's ready.
|
|
|
|
### Client-side data fetching
|
|
|
|
In scenarios where client fetching is needed, you can call `fetch` in `useEffect` (not recommended), or lean on popular React libraries in the community (such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/latest)) for client fetching.
|
|
|
|
```tsx filename="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>
|
|
)
|
|
}
|
|
```
|