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

React Hook Form错误提示Toast为何会重复触发一次?

React-Hook-Form Toast误触发问题排查与解决

问题描述

我使用react-hook-form做字段校验,除了将字段设为必填项外,还通过toast提示用户填写必填字段。实现的函数如下:

const runError = () => {
    if (!isValid && Object.keys(errors).length > 0) {
      toast.error("请填写必填字段", {
        icon: <ErrorIcon />,
        closeButton: <CloseIcon />,
      });
      return <div></div>;
    } else {
      return null;
    }
  };

我在JSX中直接调用{runError()},功能正常但存在问题:当填写完所有必填字段后,toast仍会被触发一次。另外我的代码包含提交逻辑:onPost={handleSubmit((data) => handleFormSubmit("post", data))},以及对应的处理函数:

const handleFormSubmit = async (
    action: "save" | "post",
    data: TestModel
  ) => {
    if (action === "save") {
      const newModel: TestDto = { ...model, ...data };
      await saveHandler(newModel);
    } else if (action === "post") {
      const newModel: TestDto = { ...model, ...data };
      await saveHandler(newModel);
      await postHandler();
    }
  };

问题原因

  1. 渲染时机导致的误执行:runError()直接写在JSX中,组件每次重渲染(比如用户输入后表单状态更新)都会执行这个函数。当填完最后一个必填字段时,表单的isValid和errors状态正在异步更新,组件同步渲染时可能读取到旧的状态值,从而触发toast。
  2. react-hook-form状态异步更新特性:表单的校验状态(isValid、errors)是异步更新的,组件渲染可能先于状态更新完成,导致runError()拿到的是未同步的旧状态,引发不必要的toast。

解决方案

方案1:用useEffect监听提交后的状态变化

不要直接在JSX中调用runError(),改用useEffect监听表单的提交状态和校验状态,仅在提交失败时触发toast:

import { useEffect } from 'react';
import { useForm } from 'react-hook-form';

// 从useForm中获取提交状态isSubmitted
const { formState: { isValid, errors, isSubmitted } } = useForm();

useEffect(() => {
  // 仅当表单被提交过,且校验不通过时触发提示
  if (isSubmitted && !isValid && Object.keys(errors).length > 0) {
    toast.error("请填写必填字段", {
      icon: <ErrorIcon />,
      closeButton: <CloseIcon />,
    });
  }
}, [isValid, errors, isSubmitted]);

之后删除JSX中的{runError()}调用即可。

方案2:使用handleSubmit的错误回调(推荐)

react-hook-form的handleSubmit支持第二个参数作为错误回调,专门处理校验失败的场景,这是官方推荐的做法,完全避免渲染时的误触发:

// 修改onPost的绑定逻辑,添加错误回调
onPost={handleSubmit(
  (data) => handleFormSubmit("post", data),
  // 校验失败时执行此回调
  () => {
    toast.error("请填写必填字段", {
      icon: <ErrorIcon />,
      closeButton: <CloseIcon />,
    });
  }
)}

这种方式下,只有用户点击提交按钮且表单校验不通过时,才会触发toast,逻辑更精准,同时可以直接删除runError()函数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 10:17:23