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(); } };
问题原因
- 渲染时机导致的误执行:
runError()直接写在JSX中,组件每次重渲染(比如用户输入后表单状态更新)都会执行这个函数。当填完最后一个必填字段时,表单的isValid和errors状态正在异步更新,组件同步渲染时可能读取到旧的状态值,从而触发toast。 - 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
相关产品推荐
相关产品推荐

