React19/Next.js15中用useActionState+Zod如何避免验证错误致表单字段重置
解决React 19 + Next.js 15中useActionState验证错误时货币选择重置的问题
问题根因
你的代码中存在两个核心问题:
- 本地表单状态(
useState管理的payment/currency)与useActionState返回的服务端状态未同步,验证错误时可能出现状态不一致 select元素上的key={currency}会导致货币切换时组件强制重新挂载,触发浏览器默认选中第一个选项的行为
修复方案
以下是修改后的完整代码,关键调整已标注:
页面组件修改
"use client"; import React, { useActionState, useState, useEffect } from "react"; import { useFormStatus } from "react-dom"; import { currencies } from "./data-schema"; import { actionPaymentSubmit } from "./actionPaymentSubmit"; function SubmitButton() { const { pending } = useFormStatus(); return <button type="submit">{pending ? "Pending..." : "Submit"}</button>; } export default function Home() { const [payment, setPayment] = useState(100); const [currency, setCurrency] = useState("EUR"); // 初始化状态改为固定值,避免依赖useState初始值 const [state, formAction] = useActionState(actionPaymentSubmit, { data: { payment: 100, currency: "EUR", }, }); // 核心修复:同步服务端返回的状态到本地表单 useEffect(() => { if (state.data) { setPayment(state.data.payment); setCurrency(state.data.currency); } }, [state]); return ( <main> <form action={formAction}> <label htmlFor="payment">Payment</label> <input id="payment_ammount" min="0" type="number" name="payment" value={payment} onChange={(e) => setPayment(Number(e.target.value))} /> <label htmlFor="currency">Currency</label> {/* 移除不必要的key属性,避免组件强制重新挂载 */} <select id="currency" name="currency" value={currency} onChange={(e) => setCurrency(e.target.value)} > {currencies.map((currency) => ( <option key={currency.value} value={currency.value}> {currency.label} </option> ))} </select> <SubmitButton /> {state.errors?.payment && <div>{state.errors.payment}</div>} {state.message && <div>{state.message}</div>} </form> </main> ); }
修复说明
- 同步服务端状态到本地:通过
useEffect监听useActionState返回的state变化,将服务端返回的data同步到本地useState,确保验证错误时表单显示的是用户提交的最后一次输入值 - 移除select的key属性:
key={currency}会导致每次货币切换时select组件被卸载后重新挂载,浏览器可能默认选中第一个选项,移除后可避免该行为 - 固定初始状态:
useActionState的初始状态改为固定值,避免依赖useState的初始值,逻辑更清晰
修改后即可实现你预期的用户流程:验证错误时货币选择保持不变,用户仅需修改金额即可重新提交。
内容的提问来源于stack exchange,提问作者poloniki
相关产品推荐
相关产品推荐

