如何更新資料

您可以使用 React 的 伺服器函式 (Server Functions) 在 Next.js 中更新資料。本頁將介紹如何建立呼叫伺服器函式。

伺服器函式

伺服器函式是在伺服器端執行的非同步函式。由於是透過客戶端發送網路請求來呼叫,伺服器函式本質上是非同步的。當作為 action 的一部分被呼叫時,它們也被稱為伺服器動作 (Server Actions)

按照慣例,action 是傳遞給 startTransition 的非同步函式。在以下情況下,伺服器函式會自動被 startTransition 包裝:

  • 透過 action 屬性傳遞給 <form>
  • 透過 formAction 屬性傳遞給 <button>
  • 傳遞給 useActionState

建立伺服器函式

可以使用 use server 指令來定義伺服器函式。您可以將指令放在非同步函式的頂部以標記該函式為伺服器函式,或放在獨立檔案的頂部以標記該檔案的所有導出。

export async function createPost(formData: FormData) {
  'use server'
  const title = formData.get('title')
  const content = formData.get('content')

  // 更新資料
  // 重新驗證快取
}

export async function deletePost(formData: FormData) {
  'use server'
  const id = formData.get('id')

  // 更新資料
  // 重新驗證快取
}
export async function createPost(formData) {
  'use server'
  const title = formData.get('title')
  const content = formData.get('content')

  // 更新資料
  // 重新驗證快取
}

export async function deletePost(formData) {
  'use server'
  const id = formData.get('id')

  // 更新資料
  // 重新驗證快取
}

伺服器元件

可以在伺服器元件中內聯伺服器函式,方法是將 "use server" 指令添加到函式主體的頂部:

export default function Page() {
  // 伺服器動作
  async function createPost(formData: FormData) {
    'use server'
    // ...
  }

  return <></>
}
export default function Page() {
  // 伺服器動作
  async function createPost(formData: FormData) {
    'use server'
    // ...
  }

  return <></>
}

客戶端元件

無法在客戶端元件中定義伺服器函式。但是,您可以透過從頂部帶有 "use server" 指令的檔案中導入它們來在客戶端元件中呼叫:

'use server'

export async function createPost() {}
'use server'

export async function createPost() {}
'use client'

import { createPost } from '@/app/actions'

export function Button() {
  return <button formAction={createPost}>建立</button>
}
'use client'

import { createPost } from '@/app/actions'

export function Button() {
  return <button formAction={createPost}>建立</button>
}

呼叫伺服器函式

有兩種主要方式可以呼叫伺服器函式:

  1. 在伺服器和客戶端元件中使用表單
  2. 在客戶端元件中使用事件處理器

表單

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">建立</button>
    </form>
  )
}
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">建立</button>
    </form>
  )
}
'use server'

export async function createPost(formData: FormData) {
  const title = formData.get('title')
  const content = formData.get('content')

  // 更新資料
  // 重新驗證快取
}
'use server'

export async function createPost(formData) {
  const title = formData.get('title')
  const content = formData.get('content')

  // 更新資料
  // 重新驗證快取
}

小知識: 當傳遞給 action 屬性時,伺服器函式也被稱為伺服器動作

事件處理器

您可以在客戶端元件中使用 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>總讚數: {likes}</p>
      <button
        onClick={async () => {
          const updatedLikes = await incrementLike()
          setLikes(updatedLikes)
        }}
      >

      </button>
    </>
  )
}
'use client'

import { incrementLike } from './actions'
import { useState } from 'react'

export default function LikeButton({ initialLikes }) {
  const [likes, setLikes] = useState(initialLikes)

  return (
    <>
      <p>總讚數: {likes}</p>
      <button
        onClick={async () => {
          const updatedLikes = await incrementLike()
          setLikes(updatedLikes)
        }}
      >

      </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 /> : '建立文章'}
    </button>
  )
}
'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 /> : '建立文章'}
    </button>
  )
}

重新驗證快取

執行更新後,您可以在伺服器函式中呼叫 revalidatePathrevalidateTag 來重新驗證 Next.js 快取並顯示更新後的資料:

import { revalidatePath } from 'next/cache'

export async function createPost(formData: FormData) {
  'use server'
  // 更新資料
  // ...

  revalidatePath('/posts')
}
import { revalidatePath } from 'next/cache'

export async function createPost(formData) {
  'use server'
  // 更新資料
  // ...
  revalidatePath('/posts')
}

重新導向

您可能希望在執行更新後將使用者重新導向到不同頁面。您可以在伺服器函式中呼叫 redirect 來實現:

'use server'

import { redirect } from 'next/navigation'

export async function createPost(formData: FormData) {
  // 更新資料
  // ...

  redirect('/posts')
}
'use server'

import { redirect } from 'next/navigation'

export async function createPost(formData) {
  // 更新資料
  // ...

  redirect('/posts')
}