React Hook Form自动提交问题:自动模式下进度完成+3秒提交
解决方案
核心问题是你通过DOM操作触发表单提交的方式没有正确对接react-hook-form的提交逻辑,直接调用react-hook-form提供的handleSubmit包装后的提交函数即可解决,同时调整自动提交的触发时机,确保每次进度完成后延迟3秒再提交。
修改步骤
直接调用react-hook-form的提交函数:
不需要通过document.querySelector('form').requestSubmit(),直接使用handleSubmit(onSubmit)()就能触发表单提交,这是react-hook-form官方推荐的程序化提交方式,完全适配表单验证和提交流程。调整自动提交的定时器逻辑:
当进度完成后(AlchemyProgressBar调用setProgressData(null)),通过监听progress状态变化,在新进度生成且自动模式开启时,重新设置延迟提交定时器,实现循环自动提交。
修改后的完整代码
组件逻辑部分
import { useState, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import Swal from 'sweetalert2'; import AlchemyService from './path-to-your-alchemy-service'; import AlchemyProgressBar from './path-to-your-AlchemyProgressBar'; import Furnace from './path-to-your-furnace-image'; const AlchemyComponent = () => { const [progress, setProgress] = useState(null); const [autoMode, setAutoMode] = useState(false); const [autoTimer, setAutoTimer] = useState(null); const { register, handleSubmit, formState: { isSubmitting } } = useForm(); // 计算延迟时间:进度时长 + 额外3秒 const calculateDelay = (startTimeStr, endTimeStr) => { const startTime = new Date(startTimeStr).getTime(); const endTime = new Date(endTimeStr).getTime(); const duration = endTime - startTime; const extraTime = 3000; // 3秒延迟 return duration + extraTime; }; // 触发表单提交的函数 const triggerFormSubmit = () => { handleSubmit(onSubmit)(); }; // 启动自动提交定时器 const startAutoSubmit = () => { if (!progress) return; const delay = calculateDelay(progress.start_time, progress.end_time); const timer = setTimeout(() => { triggerFormSubmit(); }, delay); setAutoTimer(timer); }; // 停止自动提交 const stopAutoSubmit = () => { if (autoTimer) { clearTimeout(autoTimer); setAutoTimer(null); } }; // 切换自动模式 const handleAutoModeToggle = () => { if (!autoMode) { startAutoSubmit(); } else { stopAutoSubmit(); } setAutoMode(!autoMode); }; // 监听进度和自动模式变化,自动启动/停止定时器 useEffect(() => { if (autoMode && progress) { startAutoSubmit(); } return () => stopAutoSubmit(); }, [autoMode, progress]); // 表单提交逻辑 const onSubmit = async (data) => { if (!data.elixir) { Swal.fire({ icon: 'error', title: 'Lỗi', text: 'Vui lòng chọn đan dược để luyện!', showConfirmButton: false, timer: 1500 }); return; } const formData = new FormData(); formData.append('elixir_id', parseInt(data.elixir)); try { const res = await AlchemyService.createElixir(formData); if (res.status === 201) { Swal.fire({ icon: 'success', title: 'Thành công', text: `Luyện thành công`, showConfirmButton: false, timer: 1500 }); // 提交成功后,根据后端返回的新进度数据更新状态(需根据实际接口调整) // setProgress(res.data.progress); } else { throw new Error(res.response?.data[0] || 'Luyện thất bại'); } } catch (error) { Swal.fire({ icon: 'error', title: 'Thất bại', text: error.message, showConfirmButton: false, timer: 1500 }); // 提交失败时自动关闭自动模式(可选,根据需求调整) stopAutoSubmit(); setAutoMode(false); } }; return ( <form onSubmit={handleSubmit(onSubmit)} className='flex flex-col gap-3 font-medium text-gray-600'> {/* 表单选项部分 */} {list.map((item, index) => ( <div key={index} className='flex flex-col gap-1'> <div className='flex items-center gap-1'> <input type='radio' {...register('elixir')} value={item.elixir.id} id={`dan-duoc-${index}`} /> <label htmlFor={`dan-duoc-${index}`} className='font-medium text-yellow-600'> <span>{item.elixir.name}</span> (độ thành thục: {item.proficiency} %) </label> </div> <p className='text-sm text-gray-600'> <i> <span className='font-bold text-gray-700'>Đan dược này cần:</span> {item.description} </i> </p> <p className='text-sm text-gray-600'> Thời gian luyện: <b>{Math.ceil(parseInt(item.elixir.crafting_time) / 60)}</b> giờ </p> </div> ))} {/* 自动模式切换按钮 */} <button type='button' onClick={handleAutoModeToggle} className={`px-2 py-1 ${autoMode ? 'bg-red-500' : 'bg-primary'} text-white font-medium rounded w-fit`} > {autoMode ? 'Tắt auto luyện đan' : 'Bật auto luyện đan'} </button> {/* 进度/提交按钮区域 */} {progress === null ? ( isSubmitting ? ( <p>Đang tải...</p> ) : ( <div className='w-full flex justify-center items-center mt-10'> <button type='submit' className='size-60'> <img src={Furnace} alt='Luyện đan' className='size-full object-contain' /> </button> </div> ) ) : ( <AlchemyProgressBar startTimeStr={progress.start_time} endTimeStr={progress.end_time} name={progress.elixir_name} setProgressData={setProgress} /> )} </form> ); }; export default AlchemyComponent;
关键说明
- 替代DOM提交:使用
handleSubmit(onSubmit)()直接触发提交,避免DOM操作带来的元素未找到、提交逻辑不匹配等问题。 - 状态驱动定时器:通过
useEffect监听progress和autoMode状态,确保每次新进度生成时自动设置延迟提交,实现循环自动提交逻辑。 - 错误处理优化:提交失败时自动关闭自动模式,避免重复失败提交,提升用户体验。
内容的提问来源于stack exchange,提问作者Jack Phat
相关产品推荐
相关产品推荐

