You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:40:22