'use server'
标记可以从客户端代码调用的服务端函数。
参考
'use server'
在异步函数体的开头添加 'use server'
,以将该函数标记为可由客户端调用。我们称这些函数为*服务器动作*。
async function addToCart(data) {
'use server';
// ...
}
当在客户端调用服务器动作时,它将向服务器发出一个网络请求,其中包含传递的任何参数的序列化副本。如果服务器动作返回一个值,该值将被序列化并返回给客户端。
您可以将指令添加到文件的顶部,而不是使用 'use server'
单独标记函数,以将该文件中的所有导出标记为可以在任何地方使用的服务器动作,包括在客户端代码中导入。
注意事项
'use server'
必须位于其函数或模块的最开始处;在任何其他代码之上,包括导入(指令上方的注释是可以的)。它们必须用单引号或双引号编写,而不能使用反引号。'use server'
只能在服务端文件中使用。生成的服务器动作可以通过 props 传递给客户端组件。请参阅支持的序列化类型。- 要从客户端代码导入服务器动作,必须在模块级别使用该指令。
- 因为底层网络调用始终是异步的,所以
'use server'
只能在异步函数上使用。 - 始终将服务器动作的参数视为不受信任的输入,并授权任何修改。请参阅安全注意事项。
- 服务器动作应该在过渡中调用。传递给
<form action>
或formAction
的服务器动作将在过渡中自动调用。 - 服务器动作专为更新服务器端状态的修改而设计;不建议将它们用于数据获取。因此,实现服务器动作的框架通常一次处理一个动作,并且没有办法缓存返回值。
安全注意事项
服务器动作的参数完全由客户端控制。为了安全起见,始终将它们视为不受信任的输入,并确保根据需要验证和转义参数。
在任何服务器动作中,请确保验证登录用户是否被允许执行该动作。
可序列化参数和返回值
由于客户端代码通过网络调用服务器操作,因此传递的任何参数都需要可序列化。
以下是服务器操作参数支持的类型
值得注意的是,以下内容不支持
- 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 并支持渐进增强,请使用 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>
</>
);
}
请注意,与大多数钩子一样,useActionState
只能在 客户端代码 中调用。
在 <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
返回的承诺。