React Hook Form+Chakra-UI弹窗:表单与按钮交互失效问题排查
问题描述
我想用Chakra-UI做一个通用弹窗,能传入任意表单,表单放在弹窗主体、按钮放在弹窗页脚。页面渲染正常无报错,但提交和错误校验完全没反应。
目标结构如下:
function PopupForm({ type = 'Delete', isOpen, onClose }) { return ( <Modal isOpen={isOpen} onClose={onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>{type}</ModalHeader> <ModalCloseButton /> <ModalBody> // <- form body, based on `type` </ModalBody> <ModalFooter> // <- form buttons, based on `type` </ModalFooter> </ModalContent> </Modal> ); } export default PopupForm;
暂时忽略通用性,我手动加入简单表单测试,使用react-hook-form的useFormContext:
import DownloadEppnImForm, { DownloadEppnImFormButtons } from 'components/forms/DownloadEppnImForm'; function PopupForm({ id, isOpen, onClose }) { const methods = useForm(); return ( <FormProvider {...methods}> ... <ModalBody> <DownloadEppnImForm /> </ModalBody> <ModalFooter> <DownloadEppnImFormButtons /> </ModalFooter> ... </FormProvider> ); } export default PopupForm;
表单组件代码如下:
export const ConnectForm = ({ children }) => { const methods = useFormContext(); return children({ ...methods }); } export const onSubmit = (values) => { return new Promise((resolve) => { setTimeout(() => { console.log('Boo!') resolve() }, 3000) }) } export const DownloadEppnImFormButtons = () => ( <ConnectForm> {({handleSubmit, isLoading, isSubmitting}) => ( <Button isLoading={isLoading || isSubmitting} onClick={handleSubmit(onSubmit)}> Submit </Button> )} </ConnectForm> ); export default function DownloadEppnImForm () { return ( <ConnectForm> {({errors, register}) => ( <form> <FormControl isInvalid={errors?.name}> <FormLabel htmlFor='name'>First name</FormLabel> <Input id='name' placeholder='name' {...register('name', { required: 'This is required', minLength: { value: 4, message: 'Minimum length should be 4' }, })} /> <FormErrorMessage> {errors?.name && errors.name.message} </FormErrorMessage> </FormControl> </form> )} </ConnectForm> ) }
现在输入“Bob”点击提交,既不显示最小长度错误提示,控制台也无提交日志。参考相关问答及react-hook-form高级用法示例实现ConnectForm包装器仍未解决问题,请问哪里出错了?为何表单和按钮无法交互?
问题分析与解决
核心有两个问题导致表单无响应:
1. 提交按钮未正确触发表单校验与提交流程
- 当前按钮用
onClick直接调用handleSubmit(onSubmit),但react-hook-form的handleSubmit需要绑定到表单的onSubmit事件,或者手动触发校验逻辑,直接调用不会自动执行校验。 - 按钮不在
<form>标签内部,无法通过默认表单提交机制触发校验。
2. useForm未配置校验触发模式
默认情况下react-hook-form的校验是延迟触发的,需要显式配置mode参数指定校验时机(如提交时、输入改变时)。
修复步骤:
步骤1:配置useForm的校验模式
修改PopupForm中的useForm初始化,开启提交时校验:
function PopupForm({ id, isOpen, onClose }) { // 添加mode: 'onSubmit',确保提交时触发所有字段校验 const methods = useForm({ mode: 'onSubmit' }); return ( <FormProvider {...methods}> {/* 原有Modal代码 */} </FormProvider> ); }
步骤2:调整按钮与表单的交互逻辑
有两种可选方案:
方案A:将按钮整合到表单内部
修改DownloadEppnImForm,把按钮放到<form>标签内并使用type="submit":
export default function DownloadEppnImForm () { return ( <ConnectForm> {({errors, register, handleSubmit, isLoading, isSubmitting}) => ( <form onSubmit={handleSubmit(onSubmit)}> <FormControl isInvalid={errors?.name}> <FormLabel htmlFor='name'>First name</FormLabel> <Input id='name' placeholder='name' {...register('name', { required: 'This is required', minLength: { value: 4, message: 'Minimum length should be 4' }, })} /> <FormErrorMessage> {errors?.name && errors.name.message} </FormErrorMessage> </FormControl> {/* 提交按钮移到form内部 */} <Button mt={4} type="submit" isLoading={isLoading || isSubmitting} > Submit </Button> </form> )} </ConnectForm> ) }
此时可删除单独的DownloadEppnImFormButtons组件,弹窗的ModalFooter可保留取消按钮等其他操作。
方案B:保持按钮在ModalFooter,手动触发校验
若必须将按钮放在弹窗页脚,可通过useFormContext获取trigger方法手动校验字段:
export const DownloadEppnImFormButtons = () => ( <ConnectForm> {({handleSubmit, trigger, isLoading, isSubmitting}) => { const handleClick = async () => { // 手动触发所有字段校验 const isValid = await trigger(); if (isValid) { // 校验通过后执行提交逻辑 await handleSubmit(onSubmit)(); } } return ( <Button isLoading={isLoading || isSubmitting} onClick={handleClick} > Submit </Button> ) }} </ConnectForm> );
步骤3:简化ConnectForm组件(可选)
当前的ConnectForm可以简化,无需解构再合并上下文:
export const ConnectForm = ({ children }) => { const methods = useFormContext(); return children(methods); }
完成以上修改后,点击提交按钮会触发校验逻辑:输入“Bob”时会显示最小长度错误,校验通过后会执行onSubmit并打印日志。
内容的提问来源于stack exchange,提问作者philolegein
相关产品推荐
相关产品推荐

