'use server'
用于标记服务器端函数,这些函数可以从客户端代码调用。
参考
'use server'
在异步函数体的顶部添加'use server'
,以将该函数标记为客户端可调用。我们将这些函数称为服务器函数。
async function addToCart(data) {
'use server';
// ...
}
当在客户端调用服务器函数时,它将向服务器发出网络请求,其中包含传递的任何参数的序列化副本。如果服务器函数返回一个值,则该值将被序列化并返回给客户端。
无需使用'use server'
单独标记函数,您可以将指令添加到文件的顶部,以将该文件中所有导出的内容标记为可以在任何地方使用的服务器函数,包括在客户端代码中导入。
注意事项
'use server'
必须位于函数或模块的开头;位于任何其他代码(包括导入)之上(指令上面的注释是可以的)。它们必须使用单引号或双引号编写,而不是反引号。'use server'
只能用于服务器端文件。生成的服务器函数可以通过 props 传递给客户端组件。请参见支持的序列化类型。- 要从客户端代码导入服务器函数,必须在模块级别使用该指令。
- 因为底层的网络调用总是异步的,
'use server'
只能用于异步函数。 - 始终将服务器函数的参数视为不受信任的输入并授权任何修改。请参见安全注意事项。
- 服务器函数应在Transition 中调用。传递给
<form action>
或formAction
的服务器函数将自动在 transition 中调用。 - 服务器函数旨在进行更新服务器端状态的变异操作;不建议将其用于数据获取。因此,实现服务器函数的框架通常一次处理一个操作,并且没有缓存返回值的方法。
安全注意事项
服务器函数的参数完全由客户端控制。出于安全考虑,始终将它们视为不受信任的输入,并确保根据需要验证和转义参数。
在任何服务器函数中,请务必验证登录用户是否有权执行该操作。
可序列化参数和返回值
由于客户端代码通过网络调用服务器函数,因此传递的任何参数都必须是可序列化的。
以下是服务器函数参数支持的类型
- 基本类型
- 字符串 (string)
- 数字 (number)
- 大整数 (bigint)
- 布尔值 (boolean)
- 未定义 (undefined)
- 空值 (null)
- symbol,仅限通过
Symbol.for
在全局 Symbol 注册表中注册的 symbol
- 包含可序列化值的迭代器
- 日期 (Date)
- FormData 实例
- 普通对象:使用对象初始化器创建,具有可序列化属性的对象
- 作为服务器函数的函数
- Promise
值得注意的是,以下类型不受支持:
- React 元素或JSX
- 函数,包括组件函数或任何其他非服务器函数
- 类
- 任何类的实例对象(上述内置类型除外)或具有空原型的对象
- 未在全局注册的 symbol,例如
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 中调用服务器函数,这允许您显示加载指示器,显示 乐观状态更新 并处理意外错误。表单会自动将服务器函数包装在转换中。
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。