'use server'
标记可以从客户端代码调用的服务器函数。
参考
'use server'
在异步函数顶部添加 'use server'
以将该函数标记为可由客户端调用。我们将这些函数称为 Server Action。
async function addToCart(data) {
'use server';
// ...
}
在客户端调用 Server Action 时,它将向服务器发出网络请求,其中包含传递的任何参数的序列化副本。如果 Server Action 返回一个值,该值将被序列化并返回给客户端。
可以将 'use server'
指示符添加到文件顶部来代替逐个在函数中添加它。位于文件顶部的指示符会将所有导出都标记为可在任何地方使用的 Server Action,包括在客户端代码中导入。
注意
'use server'
必须位于函数或模块的顶部,在任何导入或其他代码之前(可以位于代码顶部的注释之后)。它们必须用单引号或双引号编写,但不能用反引号。'use server'
只能在服务器端文件中使用。生成的 Server Action 可以通过 props 传递给客户端组件。请参阅支持的 序列化参数和返回值类型。- 要从 客户端代码 导入 Server Action,必须在模块级别使用该指示符。
- 由于底层网络调用始终是异步的,
'use server'
只能用于异步函数。 - 始终将 Server Action 的参数视为不受信任的输入,并授权任何变更。请参阅 安全考虑。
- 应在 transition 中调用 Server Action。传递给
<form action>
或formAction
的 Server Action 将自动在 transition 中被调用。 - Server Action 专为更新服务器端状态的变更而设计,不建议用于数据获取。因此,实现 Server Action 的框架通常一次只处理一个 Action,没有缓存返回值的方式。
安全考虑
Server Action 的参数完全由客户端控制。出于安全考虑,始终将它们视为不受信任的输入,并确保根据需要验证和转义参数。
在任何 Server Action 中,请确保验证已登录的用户是否被允许执行该操作。
可序列化参数和返回值
当客户端代码通过网络调用 Server Action 时,传递的任何参数都必须是可序列化的。
以下是 Server Action 参数支持的类型:
值得注意的是,以下内容不受支持:
- React 元素或 JSX
- 函数,包括组件函数和其他并非 Server Action 的函数
- 类
- 任何类的实例对象(除了提到的内置类)或 使用 null 作为原型 的对象
- 未全局注册的符号,例如
Symbol('my new symbol')
支持的可序列化返回值与边界客户端组件的 可序列化 props 相同。
用法
表格中的 Server Action
Server Action 的最常见用法将是调用会更改数据的服务器函数。在浏览器中,HTML form 元素 是用户提交变更的传统方法。通过 React 服务器组件,React 在 表单 中首次引入了对 Server Action 的一流支持。
以下是一个允许用户请求用户名的表单。
// 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>
的 Server Action。当用户提交此表单时,会发起一个网络请求到服务器函数 requestUsername
。在调用表单中的 Server Action 时,React 将 FormData
作为第一个参数提供给 Server Action。
通过将 Server Action 传递给表单的 action
,React 可以 逐步增强 表单。这意味着表单可以在 JavaScript 捆绑加载之前提交。
处理表单中的返回值
在用户名请求表单中,可能存在用户名不可用的情况。requestUsername
应该告诉我们它是否失败。
请使用 useFormState
以根据 Server Action 的结果更新 UI 并支持逐步增强。
// 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">请求</button>
</form>
<p>最后一次提交的请求的返回值: {returnValue}</p>
</>
);
}
请注意,与大多数 Hook 一样,useFormState
只能在 客户端代码 中调用。
在 <form>
之外调用 Server Action
Server Action 是暴露的服务器端点,可以在客户端代码的任何位置调用。
在 <form>
之外使用 Server Action 时,调用 Server Action 时请使用 transition,这允许显示加载指示器、显示 乐观状态更新 和处理意外错误。表单会自动在 transition 中包装 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>点赞数量: {likeCount}</p>
<button onClick={onClick} disabled={isPending}>点赞</button>;
</>
);
}
// actions.js
'use server';
let likeCount = 0;
export default async function incrementLike() {
likeCount++;
return likeCount;
}
可以使用 await
获取 Promise 的返回值以读取 Server Action 的返回值。