更新数据
¥Updating Data
你可以使用 React 的 服务器函数 更新 Next.js 中的数据。本页将介绍如何使用 create 和 invoke 服务器功能。
¥You can update data in Next.js using React's Server Functions. This page will go through how you can create and invoke Server Functions.
什么是服务器函数?
¥What are Server Functions?
服务器函数是在服务器上运行的异步函数。它们可以通过网络请求从客户端调用,因此它们必须是异步的。
¥A Server Function is an asynchronous function that runs on the server. They can be called from client through a network request, which is why they must be asynchronous.
在 action
或突变上下文中,它们也称为服务器操作。
¥In an action
or mutation context, they are also called Server Actions.
按照惯例,服务器操作是与 startTransition
一起使用的异步函数。当函数处于以下情况时,此操作会自动发生:
¥By convention, a Server Action is an async function used with startTransition
. This happens automatically when the function is:
使用
action
属性传递给<form>
。¥Passed to a
<form>
using theaction
prop.使用
formAction
属性传递给<button>
。¥Passed to a
<button>
using theformAction
prop.
在 Next.js 中,服务器操作与框架的 caching 架构集成。调用操作时,Next.js 可以在单个服务器往返中返回更新的 UI 和新数据。
¥In Next.js, Server Actions integrate with the framework's caching architecture. When an action is invoked, Next.js can return both the updated UI and new data in a single server roundtrip.
在幕后,操作使用 POST
方法,并且只有此 HTTP 方法可以调用它们。
¥Behind the scenes, actions use the POST
method, and only this HTTP method can invoke them.
创建服务器函数
¥Creating Server Functions
可以使用 use server
指令定义服务器函数。你可以将指令放在异步函数的顶部以将该函数标记为服务器函数,或者放在单独文件的顶部以标记该文件的所有导出。
¥A Server Function can be defined by using the use server
directive. You can place the directive at the top of an asynchronous function to mark the function as a Server Function, or at the top of a separate file to mark all exports of that file.
export async function createPost(formData: FormData) {
'use server'
const title = formData.get('title')
const content = formData.get('content')
// Update data
// Revalidate cache
}
export async function deletePost(formData: FormData) {
'use server'
const id = formData.get('id')
// Update data
// Revalidate cache
}
服务器组件
¥Server Components
可以通过将 "use server"
指令添加到函数主体顶部,在服务器组件中内联服务器函数:
¥Server Functions can be inlined in Server Components by adding the "use server"
directive to the top of the function body:
export default function Page() {
// Server Action
async function createPost(formData: FormData) {
'use server'
// ...
}
return <></>
}
需要了解:服务器组件默认支持渐进式增强,这意味着即使 JavaScript 尚未加载或被禁用,调用服务器操作的表单也会被提交。
¥Good to know: Server Components support progressive enhancement by default, meaning forms that call Server Actions will be submitted even if JavaScript hasn't loaded yet or is disabled.
客户端组件
¥Client Components
无法在客户端组件中定义服务器函数。但是,你可以通过从顶部包含 "use server"
指令的文件中导入它们来在客户端组件中调用它们:
¥It's not possible to define Server Functions in Client Components. However, you can invoke them in Client Components by importing them from a file that has the "use server"
directive at the top of it:
'use server'
export async function createPost() {}
'use client'
import { createPost } from '@/app/actions'
export function Button() {
return <button formAction={createPost}>Create</button>
}
需要了解:在客户端组件中,如果 JavaScript 尚未加载,调用服务器操作的表单将对提交进行排队,并优先进行数据同步。水合后,浏览器在提交表单时不会刷新。
¥Good to know: In Client Components, forms invoking Server Actions will queue submissions if JavaScript isn't loaded yet, and will be prioritized for hydration. After hydration, the browser does not refresh on form submission.
将操作作为 props 传递
¥Passing actions as props
你还可以将操作作为 prop 传递给客户端组件:
¥You can also pass an action to a Client Component as a prop:
'use client'
export default function ClientComponent({
updateItemAction,
}: {
updateItemAction: (formData: FormData) => void
}) {
return <form action={updateItemAction}>{/* ... */}</form>
}
调用服务器函数
¥Invoking Server Functions
有两种主要方式可以调用服务器函数:
¥There are two main ways you can invoke a Server Function:
服务器和客户端组件中的 表单
¥Forms in Server and Client Components
¥Event Handlers and useEffect in Client Components
表单
¥Forms
React 扩展了 HTML <form>
元素,允许使用 HTML action
属性调用服务器函数。
¥React extends the HTML <form>
element to allow Server Function to be invoked with the HTML action
prop.
在表单中调用时,该函数会自动接收 FormData
对象。你可以使用原生 FormData
方法 提取数据:
¥When invoked in a form, the function automatically receives the FormData
object. You can extract the data using the native FormData
methods:
import { createPost } from '@/app/actions'
export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<input type="text" name="content" />
<button type="submit">Create</button>
</form>
)
}
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title')
const content = formData.get('content')
// Update data
// Revalidate cache
}
事件处理程序
¥Event Handlers
你可以使用事件处理程序(例如 onClick
)在客户端组件中调用服务器函数。
¥You can invoke a Server Function in a Client Component by using event handlers such as onClick
.
'use client'
import { incrementLike } from './actions'
import { useState } from 'react'
export default function LikeButton({ initialLikes }: { initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes)
return (
<>
<p>Total Likes: {likes}</p>
<button
onClick={async () => {
const updatedLikes = await incrementLike()
setLikes(updatedLikes)
}}
>
Like
</button>
</>
)
}
示例
¥Examples
显示待处理状态
¥Showing a pending state
在执行服务器函数时,你可以使用 React 的 useActionState
钩子显示加载指示器。此钩子返回 pending
布尔值:
¥While executing a Server Function, you can show a loading indicator with React's useActionState
hook. This hook returns a pending
boolean:
'use client'
import { useActionState, startTransition } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'
export function Button() {
const [state, action, pending] = useActionState(createPost, false)
return (
<button onClick={() => startTransition(action)}>
{pending ? <LoadingSpinner /> : 'Create Post'}
</button>
)
}
重新验证
¥Revalidating
执行更新后,你可以通过在服务器函数中调用 revalidatePath
或 revalidateTag
来重新验证 Next.js 缓存并显示更新的数据:
¥After performing an update, you can revalidate the Next.js cache and show the updated data by calling revalidatePath
or revalidateTag
within the Server Function:
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
'use server'
// Update data
// ...
revalidatePath('/posts')
}
重定向
¥Redirecting
你可能希望在执行更新后将用户重定向到其他页面。你可以通过在服务器函数中调用 redirect
来执行此操作:
¥You may want to redirect the user to a different page after performing an update. You can do this by calling redirect
within the Server Function:
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// Update data
// ...
redirect('/posts')
}
Cookies
你可以使用 cookies
API 在服务器操作中设置 get
、set
和 delete
cookie:
¥You can get
, set
, and delete
cookies inside a Server Action using the cookies
API:
'use server'
import { cookies } from 'next/headers'
export async function exampleAction() {
const cookieStore = await cookies()
// Get cookie
cookieStore.get('name')?.value
// Set cookie
cookieStore.set('name', 'Delba')
// Delete cookie
cookieStore.delete('name')
}
useEffect
当组件安装或依赖更改时,你可以使用 React useEffect
钩子来调用服务器操作。这对于依赖全局事件或需要自动触发的突变非常有用。例如,onKeyDown
用于应用快捷方式,用于无限滚动的交叉观察者钩子,或者当组件安装以更新视图计数时:
¥You can use the React useEffect
hook to invoke a Server Action when the component mounts or a dependency changes. This is useful for mutations that depend on global events or need to be triggered automatically. For example, onKeyDown
for app shortcuts, an intersection observer hook for infinite scrolling, or when the component mounts to update a view count:
'use client'
import { incrementViews } from './actions'
import { useState, useEffect, useTransition } from 'react'
export default function ViewCount({ initialViews }: { initialViews: number }) {
const [views, setViews] = useState(initialViews)
const [isPending, startTransition] = useTransition()
useEffect(() => {
startTransition(async () => {
const updatedViews = await incrementViews()
setViews(updatedViews)
})
}, [])
// You can use `isPending` to give users feedback
return <p>Total Views: {views}</p>
}