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

React19/Next.js15中用useActionState+Zod如何避免验证错误致表单字段重置

解决React 19 + Next.js 15中useActionState验证错误时货币选择重置的问题

问题根因

你的代码中存在两个核心问题:

  1. 本地表单状态(useState管理的payment/currency)与useActionState返回的服务端状态未同步,验证错误时可能出现状态不一致
  2. 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>
  );
}

修复说明

  1. 同步服务端状态到本地:通过useEffect监听useActionState返回的state变化,将服务端返回的data同步到本地useState,确保验证错误时表单显示的是用户提交的最后一次输入值
  2. 移除select的key属性:key={currency}会导致每次货币切换时select组件被卸载后重新挂载,浏览器可能默认选中第一个选项,移除后可避免该行为
  3. 固定初始状态:useActionState的初始状态改为固定值,避免依赖useState的初始值,逻辑更清晰

修改后即可实现你预期的用户流程:验证错误时货币选择保持不变,用户仅需修改金额即可重新提交。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 11:45:16