在NextJS 14中使用useFormState时如何为PATCH请求传递userId?
在Next.js 14中用useFormState传递userId到服务端动作的正确方式
你的思路确实绕远了,而且禁用的表单字段不会被包含在FormData中,所以你原来的方法其实根本传不过去userId,更别说暴露ID的问题了。下面是几种更安全且高效的方案:
方案1:通过闭包包装服务端动作(最直接)
组件中可以直接拿到路由参数params.userId,把它通过闭包传递给服务端动作,完全不会暴露给前端页面:
组件代码
'use client'; import { useFormState } from 'react-dom'; import { updateUser } from '@/actions/user'; export default function UserProfile({ params }: { params: { userId: string } }) { // 包装服务端动作,注入userId const boundUpdateUser = async (formData: FormData) => { return updateUser(formData, params.userId); }; const [state, formAction] = useFormState(boundUpdateUser, null); return ( <form action={formAction}> {/* 只渲染需要用户输入的字段,不用加隐藏/禁用的ID字段 */} <div> <label>用户名</label> <input type="text" name="username" defaultValue="" /> </div> <div> <label>邮箱</label> <input type="email" name="email" defaultValue="" /> </div> <button type="submit">更新资料</button> {state?.error && <p className="text-red-500">{state.error}</p>} {state?.success && <p className="text-green-500">{state.success}</p>} </form> ); }
服务端动作代码(app/actions/user.ts)
'use server'; export async function updateUser(formData: FormData, userId: string) { // 提取表单数据 const username = formData.get('username') as string; const email = formData.get('email') as string; // 调用PATCH接口,直接使用传入的userId const res = await fetch(`/api/user/${userId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, email }), }); if (!res.ok) { return { error: '更新失败,请稍后重试' }; } return { success: '资料更新成功' }; }
方案2:从Session中获取用户ID(更安全,适用于用户只能修改自己资料的场景)
如果用户只能更新自己的资料,完全不需要从路由或表单传ID,直接在服务端动作中通过会话获取当前用户ID,彻底避免ID暴露:
服务端动作代码
'use server'; import { getServerSession } from 'next-auth/next'; import { authOptions } from '@/lib/auth'; // 你的NextAuth配置 export async function updateUser(formData: FormData) { // 获取当前用户会话 const session = await getServerSession(authOptions); if (!session?.user?.id) { return { error: '未授权,请先登录' }; } const userId = session.user.id; // 后续表单处理和接口调用同方案1 }
组件代码
组件里直接使用原服务端动作即可,不需要包装:
'use client'; import { useFormState } from 'react-dom'; import { updateUser } from '@/actions/user'; export default function UserProfile() { const [state, formAction] = useFormState(updateUser, null); return ( <form action={formAction}> {/* 表单字段同上 */} </form> ); }
为什么原来的方法不可行?
- 浏览器会自动忽略
disabled属性的表单字段,不会将其加入FormData,所以你的userId根本传不到服务端动作里。 - 即使改用
hidden字段(去掉disabled),userId会明文出现在HTML源码中,存在安全风险。
内容的提问来源于stack exchange,提问作者Garrett
相关产品推荐
相关产品推荐

