用于数据变更的服务器操作
定义 'use server' 函数处理表单变更,并使用 revalidatePath 使缓存失效。
用于数据变更的服务器操作 是 CoddyKit 上的免费 React Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。
什么是服务器操作
服务器操作是使用 'use server' 标记的异步函数,只在服务器上运行。它们可以处理表单提交和数据变更,而无需编写 API 路由处理程序。
定义服务器操作
在函数顶部(或文件顶部)添加 'use server',即可创建服务器操作。它只能定义在服务器组件中,或专用的 actions.ts 文件中。
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.post.create({ data: { title } });
revalidatePath('/blog');
redirect('/blog');
}在表单中使用操作
将操作函数传递给表单的 action 属性。不需要 onSubmit,也不需要事件处理程序,Next.js 会序列化表单,并在服务器端调用该操作。
import { createPost } from './actions';
export default function NewPostForm() {
return (
<form action={createPost}>
<input name="title" placeholder="Post title" required />
<button type="submit">Create</button>
</form>
);
}useActionState 钩子
useActionState(React 19)会包装一个操作,以跟踪等待状态,并在组件中接收该操作的返回值。
'use client';
import { useActionState } from 'react';
import { createPost } from './actions';
const initialState = { error: null };
export function PostForm() {
const [state, action, isPending] = useActionState(createPost, initialState);
return (
<form action={action}>
{state.error && <p className="error">{state.error}</p>}
<input name="title" />
<button disabled={isPending}>{isPending ? 'Creating...' : 'Create'}</button>
</form>
);
}返回验证错误
让操作返回一个对象,而不是抛出错误,即可通过 useActionState 将验证错误传回表单。
export async function createPost(prevState: any, formData: FormData) {
'use server';
const title = formData.get('title') as string;
if (!title || title.length < 3) {
return { error: 'Title must be at least 3 characters' };
}
await db.post.create({ data: { title } });
revalidatePath('/blog');
redirect('/blog');
}事件处理程序中的服务器操作
您可以从客户端组件的事件处理程序中调用服务器操作,而不仅限于从表单操作中调用。只需导入并直接调用它们即可。
'use client';
import { deletePost } from './actions';
export function DeleteButton({ id }: { id: string }) {
return (
<button onClick={() => deletePost(id)}>
Delete
</button>
);
}内联服务器操作
在服务器组件中使用函数内的 'use server' 定义内联服务器操作,这适合将数据变更逻辑放在使用位置附近。
export default function PostPage({ post }) {
async function updatePost(formData: FormData) {
'use server';
await db.post.update({ where: { id: post.id }, data: { title: formData.get('title') as string } });
revalidatePath(`/blog/${post.id}`);
}
return (
<form action={updatePost}>
<input name="title" defaultValue={post.title} />
<button type="submit">Save</button>
</form>
);
}将 useOptimistic 与服务器操作结合使用
将 useOptimistic 与服务器操作配合使用,可以在操作于后台运行时立即更新界面。
'use client';
import { useOptimistic } from 'react';
import { addItem } from './actions';
export function TodoList({ items }) {
const [optimisticItems, addOptimistic] = useOptimistic(items, (state, newItem) => [...state, newItem]);
return (
<form action={async (formData) => {
addOptimistic({ id: 'temp', text: formData.get('text') });
await addItem(formData);
}}>
<ul>{optimisticItems.map(i => <li key={i.id}>{i.text}</li>)}</ul>
<input name="text" /><button>Add</button>
</form>
);
}安全注意事项
服务器操作在底层是 POST 端点。请始终在服务器端验证并清理输入,检查身份验证,并使用 CSRF 防护(对于同源请求,Next.js 会自动处理)。
数据变更后重新验证
在数据变更后调用 revalidatePath() 或 revalidateTag(),清除相关缓存,使下一次页面加载能够反映变更。
快速检查
在 Next.js App Router 中,如何将服务器操作传递给表单?
回顾
服务器操作是使用 'use server' 标记、在服务器端处理数据变更的异步函数。通过 action 属性将它们连接到表单,使用 useActionState 跟踪状态,在验证失败时返回错误而不是抛出错误,并在数据变更后重新验证缓存。
常见问题解答
「用于数据变更的服务器操作」课时是免费的吗?
是的 — 「用于数据变更的服务器操作」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。
「用于数据变更的服务器操作」这节课中我会学到什么?
定义 'use server' 函数处理表单变更,并使用 revalidatePath 使缓存失效。 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「用于数据变更的服务器操作」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Academy 课中编写并运行代码吗?
能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。