'use server'
標記可從用戶端程式碼呼叫的伺服器端函數。
參考
'use server'
在非同步函數主體最上方加入 'use server'
,標記該函數可由用戶端呼叫。我們將這些函數稱為伺服器動作。
async function addToCart(data) {
'use server';
// ...
}
在用戶端呼叫伺服器動作時,它會向伺服器發出網路要求,其中包含傳遞的任何參數的序列化副本。如果伺服器動作傳回值,該值將會序列化並傳回用戶端。
除了個別使用 'use server'
標記函數,您也可以將指令加入檔案最上方,將該檔案中的所有匯出標記為伺服器動作,這些動作可以在任何地方使用,包括在用戶端程式碼中匯入。
注意事項
'use server'
必須置於其函式或模組的最開頭;在任何其他程式碼(包括指令上方的註解)之前。它們必須以單引號或雙引號撰寫,而非反引號。'use server'
只能用於伺服器端檔案。產生的伺服器動作可透過道具傳遞給用戶端元件。請參閱支援的 序列化類型。- 若要從 用戶端程式碼 匯入伺服器動作,指令必須用於模組層級。
- 由於底層網路呼叫總是異步的,
'use server'
只能用於非同步函式。 - 始終將伺服器動作的引數視為不可信賴的輸入,並授權任何變異。請參閱 安全性考量。
- 應在 轉場 中呼叫伺服器動作。傳遞給
<form action>
或formAction
的伺服器動作將自動在轉場中呼叫。 - 伺服器動作是為更新伺服器端狀態的變異而設計的;不建議用於資料擷取。因此,實作伺服器動作的框架通常一次處理一個動作,並且沒有辦法快取回傳值。
安全性考量
傳遞給伺服器動作的參數完全由用戶端控制。為確保安全性,請務必將它們視為不可信賴的輸入,並適當地驗證和轉譯參數。
在任何伺服器動作中,請務必驗證已登入的使用者是否有權執行該動作。
可序列化參數和傳回值
當用戶端程式碼透過網路呼叫伺服器動作時,傳遞的任何參數都需要可序列化。
以下是伺服器動作參數支援的類型
特別注意的是,這些不受支援
- React 元素,或 JSX
- 函式,包括元件函式或任何其他非伺服器動作的函式
- 類別
- 任何類別(除了上述內建類別)的物件執行個體,或具有 null 原型 的物件
- 未在全域註冊的符號,例如
Symbol('my new symbol')
支援的序列化回傳值與 可序列化道具 相同,適用於邊界 Client Component。
用法
表單中的伺服器動作
伺服器動作最常見的用例是呼叫會變更資料的伺服器函式。在瀏覽器上,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,同時支援漸進式增強,請使用 useFormState
。
// 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 { useFormState } from 'react-dom';
import requestUsername from './requestUsername';
function UsernameForm() {
const [returnValue, action] = useFormState(requestUsername, 'n/a');
return (
<>
<form action={action}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
<p>Last submission request returned: {returnValue}</p>
</>
);
}
請注意,與大多數 Hook 一樣,useFormState
只能在 用戶端程式碼 中呼叫。
在 <form>
外呼叫伺服器動作
伺服器動作是公開的伺服器端點,可以在用戶端程式碼的任何地方呼叫。
在 表單 外使用伺服器動作時,請在 轉換 中呼叫伺服器動作,這可讓您顯示載入指示器、顯示 樂觀狀態更新,並處理意外錯誤。表單會自動將伺服器動作包裝在轉換中。
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
傳回的承諾。