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

金丝雀

'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 的服务器操作将自动在过渡中调用。
  • 服务器操作旨在用于更新服务器端状态的变异;不建议用于数据获取。因此,实现服务器操作的框架通常一次处理一个操作,并且没有缓存返回值的方法。

安全注意事项

服务器操作的参数完全由客户端控制。出于安全考虑,始终将它们视为不受信任的输入,并确保根据需要验证和转义参数。

在任何服务器操作中,请确保验证登录用户是否有权执行该操作。

正在建设中

为了防止从服务器操作发送敏感数据,有一些实验性的污染 API 可以防止将唯一值和对象传递给客户端代码。

参见 experimental_taintUniqueValueexperimental_taintObjectReference.

可序列化参数和返回值

由于客户端代码通过网络调用服务器操作,因此传递的任何参数都需要是可序列化的。

以下是服务器操作参数支持的类型

值得注意的是,这些不受支持

  • React 元素,或 JSX
  • 函数,包括组件函数或任何其他非服务器操作函数
  • 任何类的实例对象(除了上面提到的内置对象)或具有 空原型 的对象
  • 未全局注册的符号,例如 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,请使用 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>
</>
);
}

请注意,与大多数 Hooks 一样,useFormState 只能在 客户端代码 中调用。

<form> 外部调用 Server Action。

Server Action 是公开的服务器端点,可以在客户端代码的任何地方调用。

表单 外部使用 Server Action 时,请在 过渡 中调用 Server Action,这使您能够显示加载指示器,显示 乐观状态更新 以及处理意外错误。表单会自动将 Server Action 包裹在过渡中。

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;
}

要读取 Server Action 返回值,您需要 await 返回的 Promise。