React Hook Form父组件提交按钮的加载状态处理问题咨询
解决React Hook Form父组件提交按钮的加载状态问题
下面提供三种可行方案,根据你的代码结构和需求选择即可:
方案1:将Form实例提升到父组件(最简洁)
把useForm的初始化逻辑移到父组件,直接获取isSubmitting状态,同时将表单所需的register方法传给子组件渲染字段。
父组件修改后代码:
import { useForm } from 'react-hook-form'; import FormEditPassword from './FormEditPassword'; const ForceEditPassword = () => { // 在父组件初始化Form实例 const { register, handleSubmit, formState: { isSubmitting } } = useForm(); // 提交逻辑写在父组件 const onSubmit = async (formData) => { try { // 替换为你的实际API调用 await fetch('/api/update-password', { method: 'POST', body: JSON.stringify(formData), headers: { 'Content-Type': 'application/json' } }); // 提交成功后的处理,比如弹窗提示、页面跳转 } catch (error) { console.error('提交失败:', error); // 错误处理,比如显示错误提示 } }; return ( <> <div className='force-edit-password d-flex flex-column justify-content-center align-items-center'> <div className='inner d-flex justify-items-center align-items-center flex-column'> {/* 把register传给子组件渲染表单字段 */} <FormEditPassword register={register} /> <button className='bg-primary rounded bottom-0 start-50 translate-middle-x' onClick={handleSubmit(onSubmit)} disabled={isSubmitting} // 根据提交状态禁用按钮 > <p className='text-light m-0 p-2 text-capitalize rounded'> {/* 根据状态切换按钮文本 */} {isSubmitting ? 'Menyimpan...' : 'Simpan'} </p> </button> </div> </div> </> ); }; export default ForceEditPassword;
子组件FormEditPassword代码:
const FormEditPassword = ({ register }) => { return ( <div> {/* 用父组件传入的register绑定表单字段 */} <input {...register('password', { required: 'Password wajib diisi' })} type="password" placeholder="Password baru" /> <input {...register('confirmPassword', { required: 'Konfirmasi password wajib diisi' })} type="password" placeholder="Konfirmasi password" /> </div> ); }; export default FormEditPassword;
方案2:通过回调函数传递提交状态(适合子组件保留提交逻辑)
如果提交逻辑必须放在子组件内部,可以通过props给子组件传递回调函数,让子组件在isSubmitting状态变化时通知父组件。
子组件FormEditPassword修改后代码:
import { useForm, useEffect } from 'react-hook-form'; const FormEditPassword = ({ onSubmitStateChange }) => { const { register, handleSubmit, formState: { isSubmitting } } = useForm(); // 监听提交状态变化,实时通知父组件 useEffect(() => { onSubmitStateChange(isSubmitting); }, [isSubmitting, onSubmitStateChange]); // 子组件内部的提交逻辑 const onSubmit = async (formData) => { try { await fetch('/api/update-password', { method: 'POST', body: JSON.stringify(formData), headers: { 'Content-Type': 'application/json' } }); } catch (error) { console.error('提交失败:', error); } }; // 暴露给父组件调用的提交方法 const triggerSubmit = handleSubmit(onSubmit); return ( <form> <input {...register('password', { required: true })} type="password" placeholder="Password baru" /> <input {...register('confirmPassword', { required: true })} type="password" placeholder="Konfirmasi password" /> </form> ); }; export default FormEditPassword;
父组件修改后代码:
import { useState, useRef } from 'react'; import FormEditPassword from './FormEditPassword'; const ForceEditPassword = () => { const [isLoading, setIsLoading] = useState(false); const childRef = useRef(null); return ( <> <div className='force-edit-password d-flex flex-column justify-content-center align-items-center'> <div className='inner d-flex justify-items-center align-items-center flex-column'> <FormEditPassword ref={childRef} onSubmitStateChange={setIsLoading} // 传递状态回调 /> <button className='bg-primary rounded bottom-0 start-50 translate-middle-x' onClick={() => childRef.current?.triggerSubmit()} disabled={isLoading} > <p className='text-light m-0 p-2 text-capitalize rounded'> {isLoading ? 'Menyimpan...' : 'Simpan'} </p> </button> </div> </div> </> ); }; export default ForceEditPassword;
方案3:通过Ref暴露提交状态(适合子组件完全封装的场景)
使用forwardRef和useImperativeHandle让父组件访问子组件的isSubmitting状态,需结合useEffect监听状态变化触发重渲染。
子组件代码:
import { useForm, forwardRef, useImperativeHandle } from 'react-hook-form'; // 用forwardRef允许父组件获取子组件Ref const FormEditPassword = forwardRef((props, ref) => { const { register, handleSubmit, formState: { isSubmitting } } = useForm(); // 暴露给父组件的方法和状态 useImperativeHandle(ref, () => ({ triggerSubmit: handleSubmit(async (formData) => { try { await fetch('/api/update-password', { method: 'POST', body: JSON.stringify(formData), headers: { 'Content-Type': 'application/json' } }); } catch (error) { console.error('提交失败:', error); } }), isSubmitting })); return ( <form> <input {...register('password', { required: true })} type="password" placeholder="Password baru" /> <input {...register('confirmPassword', { required: true })} type="password" placeholder="Konfirmasi password" /> </form> ); }); export default FormEditPassword;
父组件代码:
import { useState, useRef, useEffect } from 'react'; import FormEditPassword from './FormEditPassword'; const ForceEditPassword = () => { const [isLoading, setIsLoading] = useState(false); const childRef = useRef(null); // 监听子组件的提交状态变化,更新父组件状态 useEffect(() => { const updateLoading = () => { setIsLoading(childRef.current?.isSubmitting || false); }; updateLoading(); // 用定时器轮询状态变化(兜底方案,推荐优先用方案2的回调) const interval = setInterval(updateLoading, 100); return () => clearInterval(interval); }, []); return ( <> <div className='force-edit-password d-flex flex-column justify-content-center align-items-center'> <div className='inner d-flex justify-items-center align-items-center flex-column'> <FormEditPassword ref={childRef} /> <button className='bg-primary rounded bottom-0 start-50 translate-middle-x' onClick={() => childRef.current?.triggerSubmit()} disabled={isLoading} > <p className='text-light m-0 p-2 text-capitalize rounded'> {isLoading ? 'Menyimpan...' : 'Simpan'} </p> </button> </div> </div> </> ); }; export default ForceEditPassword;
内容的提问来源于stack exchange,提问作者Yusni Anggara
相关产品推荐
相关产品推荐

