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

使用Server Action时无法读取Shadcn Form Select选中值的问题

解决Shadcn UI Select组件值无法在Next.js Server Action中读取的问题

问题说明

开发邮件发送表单时,使用Shadcn UI的Select组件、React Hook Form(RHF)和Next.js Server Action实现服务端验证,输入框、文本域等字段的值可正常读取,但Select组件的选中值无法在Server Action的FormData中获取。

原因分析

Shadcn UI的Select组件基于Radix UI构建,默认不会自动生成用于表单提交的<input>元素,而Next.js Server Action通过原生FormData获取字段值时,依赖表单内的标准表单控件(如input、select等)来收集数据。RHF的FormField虽能管理Select的状态,但如果没有对应的表单控件绑定,提交时该字段不会被加入FormData。

解决方案

在FormField内部添加一个隐藏的<input>元素,将其与RHF的field属性关联,让RHF自动同步Select的选中值到这个input,表单提交时该值就会被包含到FormData中。

修改后的表单组件代码

'use client'

import * as z from "zod"
import React from 'react'
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
 
import { Button } from "@/components/ui/button"
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import {toast} from "react-hot-toast"

import { sendEmail} from "@/actions/integrations/Email.actions"
import EmailSubmitButton from "./EmailSubmitButton"
import { useFormState } from "react-dom"
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"


const formSchema = z.object({
  from: z.string().email(),
  to: z.string().email(),
  subject: z.string().min(3, {
    message: "主题不能为空且至少3个字符。",
  }),
  message: z.string().min(10, {
    message: "消息不能为空且至少10个字符。",
  }),
  calendlylink : z.string()
})


const EmailApplicantForm = () => {

  const initialState = {message  : null, errors : { } };
  const [state, dispatch] = useFormState(sendEmail, initialState)
  
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      from: "test@gmail.com", 
      to: "test123@gmail.com",
      subject: "",
      message: "",
      calendlylink : ""
    },
  })

  type InterviewLinkType = {
    id : string,
    title : string,
    link : string
  }
  
  const interviewLinks: InterviewLinkType[] = [
    {
      id : '1',
      title : '前端工程师第一轮面试',
      link : 'https://calendly.com/sobitp59/interview-round-1',
    },
    {
      id : '2',
      title : '后端工程师第二轮面试',
      link : 'https://calendly.com/sobitp59/interview-for-backend-engineer-round-2',
    },
    {
      id : '3',
      title : '后端工程师第三轮面试',
      link : 'https://calendly.com/sobitp59/interview-for-backend-engineer-round-3',
    },
    {
      id : '4',
      title : '后端工程师第四轮面试',
      link : 'https://calendly.com/sobitp59/interview-for-backend-engineer-round-4',
    },
  ]


  return (
    <Form {...form}>
      <form action={dispatch} className="space-y-8">
        {/* 其他字段 */}
        
        <FormField
          control={form.control}
          name="calendlylink"
          render={({ field }) => (
            <FormItem>
              <FormLabel>选择Calendly链接</FormLabel>
              {/* 添加隐藏input同步Select值到FormData */}
              <input
                type="hidden"
                {...field}
              />
              <Select onValueChange={field.onChange} value={field.value}>
                <FormControl>
                  <SelectTrigger>
                    <SelectValue placeholder="选择你的Calendly链接"/>
                  </SelectTrigger>
                </FormControl>
                <SelectContent>
                  {interviewLinks.map(({id, title, link} : InterviewLinkType) => (
                    <SelectItem key={id.toString()} value={link}>{title}</SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <FormMessage />
            </FormItem>
          )}
        />

        <div className="flex justify-end">
          <EmailSubmitButton />
        </div>
        
      </form>
    </Form>
  )
}

export default EmailApplicantForm

代码说明

  1. 在FormField的渲染逻辑中新增隐藏<input>,通过{...field}绑定RHF的字段属性,包括name、value和ref,确保值能同步到表单提交数据中。
  2. 隐藏input不会影响页面UI,仅用于在表单提交时将Select的选中值以calendlylink为键名传入FormData。
  3. 保留原Select组件的交互逻辑,确保用户选择体验不受影响。

验证效果

修改后提交表单,Server Action中通过formData.get('calendlylink')即可获取到Select的选中值,Zod验证也能正常通过。

内容的提问来源于stack exchange,提问作者Sobit Prasad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 21:54:52