跳到内容

如何更新数据

你可以在 Next.js 中使用 React 的 服务器函数来更新数据。本页将介绍如何创建调用服务器函数。

创建服务器函数

服务器函数可以通过使用 use server 指令来定义。你可以将该指令放在一个异步函数的顶部,以将该函数标记为服务器函数,或者放在一个单独文件的顶部,以标记该文件的所有导出。

app/lib/actions.ts
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" 指令,内联在服务器组件中

app/page.tsx
export default function Page() {
  // Server Action
  async function createPost(formData: FormData) {
    'use server'
    // ...
  }
 
  return <></>
}

客户端组件

无法在客户端组件中定义服务器函数。但是,你可以在客户端组件中调用它们,方法是从顶部带有 "use server" 指令的文件中导入它们

app/actions.ts
'use server'
 
export async function createPost() {}
app/ui/button.tsx
'use client'
 
import { createPost } from '@/app/actions'
 
export function Button() {
  return <button formAction={createPost}>Create</button>
}

调用服务器函数

有两种主要方法可以调用服务器函数

  1. 服务器和客户端组件中的表单
  2. 客户端组件中的事件处理程序

表单

React 扩展了 HTML <form> 元素,以允许使用 HTML action 属性调用服务器函数。

当在表单中调用时,该函数会自动接收 FormData 对象。你可以使用原生的 FormData 方法提取数据

app/ui/form.tsx
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>
  )
}
app/actions.ts
'use server'
 
export async function createPost(formData: FormData) {
  const title = formData.get('title')
  const content = formData.get('content')
 
  // Update data
  // Revalidate cache
}

须知: 当传递给 action 属性时,服务器函数也称为服务器操作

事件处理程序

你可以在客户端组件中使用事件处理程序(例如 onClick)来调用服务器函数。

app/like-button.tsx
'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 Hook 显示加载指示器。此 Hook 返回一个 pending 布尔值

app/ui/button.tsx
'use client'
 
import { useActionState } 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={async () => action()}>
      {pending ? <LoadingSpinner /> : 'Create Post'}
    </button>
  )
}

重新验证缓存

执行更新后,你可以通过在服务器函数中调用 revalidatePathrevalidateTag 来重新验证 Next.js 缓存并显示更新后的数据

app/lib/actions.ts
import { revalidatePath } from 'next/cache'
 
export async function createPost(formData: FormData) {
  'use server'
  // Update data
  // ...
 
  revalidatePath('/posts')
}

重定向

你可能希望在执行更新后将用户重定向到不同的页面。你可以通过在服务器函数中调用 redirect 来实现

app/lib/actions.ts
'use server'
 
import { redirect } from 'next/navigation'
 
export async function createPost(formData: FormData) {
  // Update data
  // ...
 
  redirect('/posts')
}