更新資料
您可以使用 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 參考
這有幫助嗎?