'use server'
標記可以從客戶端程式碼呼叫的伺服器端函式。
參考
'use server'
在非同步函式主體的最上方新增 'use server'
指令,將函式標記為可由客戶端呼叫。我們稱這些函式為「伺服器函式」。
async function addToCart(data) {
'use server';
// ...
}
在客戶端呼叫伺服器函式時,它會向伺服器發出網路請求,其中包含傳遞的任何參數的序列化副本。如果伺服器函式返回值,則該值將被序列化並返回給客戶端。
除了在每個函式上個別標記 'use server'
之外,您也可以將指令新增到檔案頂端,將該檔案中的所有匯出項目標記為可在任何地方使用的伺服器函式,包括在客戶端程式碼中匯入。
注意事項
'use server'
必須位於函式或模組的最開頭;在任何其他程式碼之前,包括匯入語句(指令上方的註釋是可以的)。它們必須用單引號或雙引號撰寫,而不是反引號。'use server'
只能在伺服器端檔案中使用。產生的伺服器函式可以透過 props 傳遞給客戶端元件。請參閱支援的 序列化類型。- 要從客戶端程式碼匯入伺服器函式,必須在模組層級使用該指令。
- 由於底層網路呼叫始終是非同步的,因此
'use server'
只能用於非同步函式。 - 始終將伺服器函式的參數視為不受信任的輸入,並授權任何異動。請參閱安全性考量。
- 伺服器函式應該在轉換中被呼叫。傳遞給 <form action> 或 formAction 的伺服器函式將會自動在轉換中被呼叫。
- 伺服器函式是用於更新伺服器端狀態的異動;不建議將其用於資料提取。因此,實作伺服器函式的框架通常一次處理一個動作,並且沒有快取返回值的方法。
安全考量
伺服器函式的參數完全由客戶端控制。為了安全起見,始終將它們視為不受信任的輸入,並確保根據需要驗證和轉譯參數。
在任何伺服器函式中,請確保驗證已登入的使用者是否被允許執行該動作。
可序列化參數和返回值
由於客戶端程式碼透過網路呼叫伺服器函式,因此傳遞的任何參數都需要可序列化。
以下是伺服器函式參數支援的類型
- 基本類型
- 字串 (string)
- 數字 (number)
- 大整數 (bigint)
- 布林值 (boolean)
- 未定義 (undefined)
- 空值 (null)
- 符號 (symbol),僅透過
Symbol.for
在全域符號註冊表中註冊的符號
- 包含可序列化值的迭代器
- 字串 (String)
- 陣列 (Array)
- 映射 (Map)
- 集合 (Set)
- 類型化陣列 (TypedArray) 和陣列緩衝區 (ArrayBuffer)
- 日期 (Date)
- FormData 實例 (FormData)
- 純物件 (objects):使用物件初始值 (object initializers) 建立,具有可序列化屬性
- 作為伺服器函式的函式
- Promise 物件
值得注意的是,以下類型不支援
- React 元素或 JSX
- 函式,包括組件函式或任何非伺服器函式的函式
- 類別 (Classes)
- 任何類別實例的物件(除了上述內建類型之外)或具有空原型 (a null prototype) 的物件
- 未全局註冊的符號,例如
Symbol('my new symbol')
- 來自事件處理程式的事件
支援的可序列化返回值與邊界客戶端組件的可序列化屬性相同。
用法
表單中的伺服器函式
伺服器函式最常見的用例是呼叫修改資料的函式。在瀏覽器上,HTML 表單元素是使用者提交修改的傳統方法。藉由 React 伺服器組件,React 在 表單 中引入了對伺服器函式作為動作的一流支援。
以下是一個允許使用者請求用戶名的表單。
// App.js
async function requestUsername(formData) {
'use server';
const username = formData.get('username');
// ...
}
export default function App() {
return (
<form action={requestUsername}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
);
}
在此範例中,requestUsername
是一個傳遞給 <form>
的伺服器函式。當使用者提交此表單時,會向伺服器函式 requestUsername
發出網路請求。在表單中呼叫伺服器函式時,React 會將表單的 FormData 作為第一個參數提供給伺服器函式。
透過將伺服器函式傳遞給表單的 action
屬性,React 可以逐步增強表單。這表示可以在載入 JavaScript 程式碼包之前提交表單。
處理表單中的返回值
在用戶名請求表單中,可能會有用戶名不可用的情況。requestUsername
應該告知我們它是否失敗。
要根據伺服器函式的結果更新 UI 並支援逐步增強,請使用 useActionState
。
// requestUsername.js
'use server';
export default async function requestUsername(formData) {
const username = formData.get('username');
if (canRequest(username)) {
// ...
return 'successful';
}
return 'failed';
}
// UsernameForm.js
'use client';
import { useActionState } from 'react';
import requestUsername from './requestUsername';
function UsernameForm() {
const [state, action] = useActionState(requestUsername, null, 'n/a');
return (
<>
<form action={action}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
<p>Last submission request returned: {state}</p>
</>
);
}
請注意,與大多數 Hook 一樣,useActionState
只能在 客戶端程式碼 中呼叫。
在 `<form>
` 之外呼叫伺服器函式
伺服器函式是公開的伺服器端點,可以在客戶端程式碼中的任何位置呼叫。
在 `表單` 之外使用伺服器函式時,請在 `Transition` 中呼叫伺服器函式,這樣可以顯示載入指示器、顯示 樂觀狀態更新,並處理非預期的錯誤。表單會自動將伺服器函式包裝在 transitions 中。
import incrementLike from './actions';
import { useState, useTransition } from 'react';
function LikeButton() {
const [isPending, startTransition] = useTransition();
const [likeCount, setLikeCount] = useState(0);
const onClick = () => {
startTransition(async () => {
const currentCount = await incrementLike();
setLikeCount(currentCount);
});
};
return (
<>
<p>Total Likes: {likeCount}</p>
<button onClick={onClick} disabled={isPending}>Like</button>;
</>
);
}
// actions.js
'use server';
let likeCount = 0;
export default async function incrementLike() {
likeCount++;
return likeCount;
}
要讀取伺服器函式的回傳值,您需要使用 await
等待回傳的 promise。