跳到內容
App Router入門更新資料

更新資料

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

什麼是伺服器函式?

伺服器函式是在伺服器上執行的非同步函式。它們可以透過網路請求從客戶端呼叫,因此必須是非同步的。

在`action`或 mutation 上下文中,它們也被稱為伺服器操作

按照慣例,伺服器操作是與`startTransition`一起使用的非同步函式。當函式符合以下情況時,這會自動發生:

  • 透過 `action` 屬性傳遞給 `
    `。
  • 透過 `formAction` 屬性傳遞給 `

在 Next.js 中,伺服器操作與框架的快取架構整合。當呼叫一個操作時,Next.js 可以在一次伺服器往返中返回更新後的 UI 和新資料。

在後臺,操作使用 `POST` 方法,並且只有這個 HTTP 方法才能呼叫它們。

建立伺服器函式

可以透過使用`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 <></>
}

友情提示:伺服器元件預設支援漸進式增強,這意味著即使 JavaScript 尚未載入或已停用,呼叫伺服器操作的表單仍將提交。

客戶端元件

無法在客戶端元件中定義伺服器函式。但是,您可以透過從頂部帶有 `“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>
}

友情提示:在客戶端元件中,如果 JavaScript 尚未載入,呼叫伺服器操作的表單將排隊提交,並優先進行注水。注水後,瀏覽器不會在表單提交時重新整理。

將操作作為 props 傳遞

您還可以將操作作為 prop 傳遞給客戶端元件。

<ClientComponent updateItemAction={updateItem} />
app/client-component.tsx
'use client'
 
export default function ClientComponent({
  updateItemAction,
}: {
  updateItemAction: (formData: FormData) => void
}) {
  return <form action={updateItemAction}>{/* ... */}</form>
}

呼叫伺服器函式

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

  1. 伺服器元件和客戶端元件中的表單
  2. 客戶端元件中的事件處理程式useEffect

友情提示:伺服器函式是為伺服器端突變設計的。客戶端目前一次排程並等待一個。這是一個實現細節,可能會改變。如果您需要並行資料獲取,請在伺服器元件中使用資料獲取,或在單個伺服器函式或路由處理程式中執行並行工作。

表單

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
}

事件處理程式

您可以透過 `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`鉤子顯示載入指示器。此鉤子返回一個 `pending` 布林值。

app/ui/button.tsx
'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`來顯示更新後的資料。

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 { 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 樹,根據需要重新渲染、掛載或解除安裝元件。客戶端狀態會為重新渲染的元件保留,如果其依賴項發生變化,效果會重新執行。

app/actions.ts
'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`、用於無限滾動的交叉觀察器鉤子,或者在元件掛載時更新檢視計數。

app/view-count.tsx
'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>
}