更新数据
您可以使用 React 的服务器函数在 Next.js 中更新数据。本页将介绍如何创建和调用服务器函数。
什么是服务器函数?
服务器函数是在服务器上运行的异步函数。它们可以通过网络请求从客户端调用,因此必须是异步的。
在`action`或 mutation 上下文中,它们也被称为服务器操作。
按照惯例,服务器操作是与`startTransition`一起使用的异步函数。当函数符合以下情况时,这会自动发生:
- 通过 `action` 属性传递给 `
- 通过 `formAction` 属性传递给 `
在 Next.js 中,服务器操作与框架的缓存架构集成。当调用一个操作时,Next.js 可以在一次服务器往返中返回更新后的 UI 和新数据。
在后台,操作使用 `POST` 方法,并且只有这个 HTTP 方法才能调用它们。
创建服务器函数
可以通过使用`use server`指令来定义服务器函数。您可以将该指令放在异步函数的顶部,以将该函数标记为服务器函数,或者放在单独文件的顶部,以标记该文件的所有导出。
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
}服务器组件
通过在函数体顶部添加 `"use server"` 指令,可以将服务器函数内联到服务器组件中。
export default function Page() {
// Server Action
async function createPost(formData: FormData) {
'use server'
// ...
}
return <></>
}友情提示:服务器组件默认支持渐进式增强,这意味着即使 JavaScript 尚未加载或已禁用,调用服务器操作的表单仍将提交。
客户端组件
无法在客户端组件中定义服务器函数。但是,您可以通过从顶部带有 `“use server”` 指令的文件导入它们来在客户端组件中调用它们。
'use server'
export async function createPost() {}'use client'
import { createPost } from '@/app/actions'
export function Button() {
return <button formAction={createPost}>Create</button>
}友情提示:在客户端组件中,如果 JavaScript 尚未加载,调用服务器操作的表单将排队提交,并优先进行注水。注水后,浏览器不会在表单提交时刷新。
将操作作为 props 传递
您还可以将操作作为 prop 传递给客户端组件。
<ClientComponent updateItemAction={updateItem} />'use client'
export default function ClientComponent({
updateItemAction,
}: {
updateItemAction: (formData: FormData) => void
}) {
return <form action={updateItemAction}>{/* ... */}</form>
}调用服务器函数
调用服务器函数主要有两种方式:
友情提示:服务器函数是为服务器端突变设计的。客户端目前一次调度并等待一个。这是一个实现细节,可能会改变。如果您需要并行数据获取,请在服务器组件中使用数据获取,或在单个服务器函数或路由处理程序中执行并行工作。
表单
React 扩展了 HTML `<form>`元素,允许使用 HTML `action` 属性调用服务器函数。
当在表单中调用时,函数会自动接收 `FormData` 对象。您可以使用原生 `FormData` 方法提取数据。
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
}事件处理程序
您可以通过 `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>
</>
)
}示例
显示待处理状态
在执行服务器函数时,您可以使用 React 的`useActionState`钩子显示加载指示器。此钩子返回一个 `pending` 布尔值。
'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>
)
}重新验证
执行更新后,您可以重新验证 Next.js 缓存,并通过在服务器函数中调用`revalidatePath`或`revalidateTag`来显示更新后的数据。
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
'use server'
// Update data
// ...
revalidatePath('/posts')
}重定向
在执行更新后,您可能希望将用户重定向到不同的页面。您可以通过在服务器函数中调用`redirect`来完成此操作。
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// Update data
// ...
revalidatePath('/posts')
redirect('/posts')
}调用 `redirect` 会抛出框架处理的控制流异常。其后的任何代码都不会执行。如果您需要新数据,请事先调用`revalidatePath`或`revalidateTag`。
Cookies
您可以使用`cookies` API 在服务器操作中`get`、`set`和`delete` cookie。
当您在服务器操作中设置或删除 cookie 时,Next.js 会在服务器上重新渲染当前页面及其布局,以便 UI 反映新的 cookie 值。
友情提示:服务器更新适用于当前的 React 树,根据需要重新渲染、挂载或卸载组件。客户端状态会为重新渲染的组件保留,如果其依赖项发生变化,效果会重新运行。
'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`、用于无限滚动的交叉观察器钩子,或者在组件挂载时更新视图计数。
'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>
}API 参考
这有帮助吗?