「'use server'」

React 伺服器元件

'use server' 指令是用於 React 伺服器元件

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


參考

'use server'

在非同步函式主體的最上方新增 'use server' 指令,將函式標記為可由客戶端呼叫。我們稱這些函式為「伺服器函式」。

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

在客戶端呼叫伺服器函式時,它會向伺服器發出網路請求,其中包含傳遞的任何參數的序列化副本。如果伺服器函式返回值,則該值將被序列化並返回給客戶端。

除了在每個函式上個別標記 'use server' 之外,您也可以將指令新增到檔案頂端,將該檔案中的所有匯出項目標記為可在任何地方使用的伺服器函式,包括在客戶端程式碼中匯入。

注意事項

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

安全考量

伺服器函式的參數完全由客戶端控制。為了安全起見,始終將它們視為不受信任的輸入,並確保根據需要驗證和轉譯參數。

在任何伺服器函式中,請確保驗證已登入的使用者是否被允許執行該動作。

建構中

為了防止從伺服器函式發送敏感資料,有一些實驗性的 taint API 可以防止唯一值和物件被傳遞到客戶端程式碼。

請參閱 experimental_taintUniqueValue 和 experimental_taintObjectReference。

可序列化參數和返回值

由於客戶端程式碼透過網路呼叫伺服器函式,因此傳遞的任何參數都需要可序列化。

以下是伺服器函式參數支援的類型

值得注意的是,以下類型不支援

  • 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。