Next.js中如何通过Server Actions处理下拉按钮并提交表单
Next.js Server Actions表单处理:下拉选框值获取与数据提交修复
问题背景
当前使用Next.js Server Actions处理表单时,其他字段数据可正常获取,但下拉选框的值无法正确传递,且需要提交包含chosenServiceId和chosenServiceName的结构化数据,目标格式如下:
{ "clientId": "clientId", "clientName": "clientName ", "clientPhone": "clientPhone", "clientStatus": "IN_PROGRESS", "chosenServiceId": "5652c359-8400-43f4-bb72-c61dd6a553ed", "chosenServiceName": "Sample Service 8", "createdAt": "2023-09-13T03:11:31.458Z" }
问题分析
- 下拉选框的
option值设置为serviceTitle,导致提交时拿到的是服务名称而非ID,无法匹配目标格式的chosenServiceId - 全局变量
clientServiceName无法在Server Actions中可靠使用(Server Actions无状态,全局变量会跨请求污染) - 注释掉的服务列表匹配代码是获取
chosenServiceName的关键逻辑,需恢复并正确调用
修复步骤
- 修正下拉选框的value属性:将
option的value改为service.serviceId,保留显示文本为service.serviceTitle,确保提交时获取服务ID - 恢复Server Actions中的服务匹配逻辑:通过提交的服务ID从服务列表中匹配对应名称,替代不可靠的全局变量
- 对齐提交数据字段名:调整字段名与目标格式一致,如将
clientServiceId改为chosenServiceId
完整修复代码
import { getServices } from "@/app/services/services/services"; import { redirect } from "next/navigation"; import React from 'react'; async function addClient(data: FormData) { 'use server'; // 提取表单数据并做类型断言 const clientName = data.get("clientName") as string; const clientPhone = data.get("clientPhone") as string; const clientEmail = data.get("clientEmail") as string; const chosenServiceId = data.get("clientService") as string; // 校验必填字段 if (!chosenServiceId) { console.error("请选择服务"); return; } // 获取服务列表并匹配选中的服务 const services = await getServices(); const selectedService = services.find( (service: { serviceTitle: string; serviceId: string }) => service.serviceId === chosenServiceId ); if (!selectedService) { console.error("选中的服务不存在"); return; } // 构造符合要求的提交数据 const clientData = { clientName, clientPhone, clientEmail, clientStatus: "IN_PROGRESS", chosenServiceId, chosenServiceName: selectedService.serviceTitle, // clientId和createdAt若由后端自动生成则无需传入,按需调整 }; const apiUrl = "http://localhost:3000/clients"; const token = process.env.TOKEN; try { const response = await fetch(apiUrl, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify(clientData), }); if (!response.ok) { throw new Error(`请求失败:${response.status}`); } const responseData = await response.json(); console.log("客户添加成功:", responseData); redirect("/"); // 提交成功后跳转至指定页面 } catch (error) { console.error("添加客户出错:", error); } } export const BookingForm = ({ services }: { services: { serviceTitle: string; serviceId: string }[] }) => { return ( <div className="main-contact-area pb-120"> <div className="container"> <div className="section-title"> <span>Contact Us</span> <h2>Drop us a message for any query</h2> <p> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque quibusdam deleniti porro praesentium. Aliquam minus quisquam velit in at nam. </p> </div> <div className="row align-items-center"> <div className="col-lg-6 col-md-12"> <div className="contact-wrap contact-pages mb-0"> <div className="contact-form"> <form action={addClient}> <div className="row"> <div className="col-lg-6 col-sm-6"> <div className="form-group"> <input type="text" name="clientName" placeholder="Name" className="form-control" id="clientName" required /> </div> </div> <div className="col-lg-6 col-sm-6"> <div className="form-group"> <input type="text" name="clientEmail" placeholder="Email" className="form-control" id="clientEmail" required /> </div> </div> <div className="col-lg-6 col-sm-6"> <div className="form-group"> <input type="text" name="clientPhone" placeholder="Phone number" className="form-control" id="clientPhone" required /> </div> </div> <div className="col-lg-6 col-sm-6"> <div className="col-12"> <select className="form-select bg-light border-0" name="clientService" style={{ height: "55px", color: "black" }} required > <option value="">Select A Service</option> {services?.length === 0 ? ( <option value="" disabled>Loading...</option> ) : ( services.map((service) => ( <option key={service.serviceId} value={service.serviceId}> {service.serviceTitle} </option> )) )} {!services && ( <option value="" disabled>Loading...</option> )} </select> </div> </div> <div className="form-group"> <button type="submit" className="default-btn btn-two" > Book </button> </div> </div> </form> </div> </div> </div> <div className="col-lg-6 col-md-12"> <div className="contact-img"> <img src="/images/contact-img.png" alt="Image" /> </div> </div> </div> </div> </div> ); };
结论
完全可以仅通过Server Actions完成需求,无需前端额外处理状态。Server Actions在服务端执行,能够安全地获取服务列表、匹配选中的服务信息,并构造符合要求的提交数据,同时避免前端暴露敏感映射逻辑。
内容的提问来源于stack exchange,提问作者Muhammad Tawil
相关产品推荐
相关产品推荐

