'use server' - This feature is available in the latest Canary

金絲雀

'use server' 僅在您使用 React Server Components或建立與它們相容的函式庫時需要。

'use server' 標記可從用戶端程式碼呼叫的伺服器端函數。


參考

'use server'

在非同步函數主體最上方加入 'use server',標記該函數可由用戶端呼叫。我們將這些函數稱為伺服器動作

async function addToCart(data) {
'use server';
// ...
}

在用戶端呼叫伺服器動作時,它會向伺服器發出網路要求,其中包含傳遞的任何參數的序列化副本。如果伺服器動作傳回值,該值將會序列化並傳回用戶端。

除了個別使用 'use server' 標記函數,您也可以將指令加入檔案最上方,將該檔案中的所有匯出標記為伺服器動作,這些動作可以在任何地方使用,包括在用戶端程式碼中匯入。

注意事項

  • 'use server' 必須置於其函式或模組的最開頭;在任何其他程式碼(包括指令上方的註解)之前。它們必須以單引號或雙引號撰寫,而非反引號。
  • 'use server' 只能用於伺服器端檔案。產生的伺服器動作可透過道具傳遞給用戶端元件。請參閱支援的 序列化類型
  • 若要從 用戶端程式碼 匯入伺服器動作,指令必須用於模組層級。
  • 由於底層網路呼叫總是異步的,'use server' 只能用於非同步函式。
  • 始終將伺服器動作的引數視為不可信賴的輸入,並授權任何變異。請參閱 安全性考量
  • 應在 轉場 中呼叫伺服器動作。傳遞給 <form action>formAction 的伺服器動作將自動在轉場中呼叫。
  • 伺服器動作是為更新伺服器端狀態的變異而設計的;不建議用於資料擷取。因此,實作伺服器動作的框架通常一次處理一個動作,並且沒有辦法快取回傳值。

安全性考量

傳遞給伺服器動作的參數完全由用戶端控制。為確保安全性,請務必將它們視為不可信賴的輸入,並適當地驗證和轉譯參數。

在任何伺服器動作中,請務必驗證已登入的使用者是否有權執行該動作。

施工中

為防止從伺服器動作傳送敏感資料,有實驗性的污染 API 可防止將唯一值和物件傳遞給用戶端程式碼。

請參閱 experimental_taintUniqueValueexperimental_taintObjectReference

可序列化參數和傳回值

當用戶端程式碼透過網路呼叫伺服器動作時,傳遞的任何參數都需要可序列化。

以下是伺服器動作參數支援的類型

特別注意的是,這些不受支援

  • 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 傳回的承諾。