如何保持React中<input type="file">字段值在页面跳转后不重置?
问题:页面导航返回后
<input type="file">字段值重置 需要确保<input type="file">字段的值在组件变更(如页面导航离开后返回)时保持不变,但当前每次离开对应页面再返回,该字段的值都会重置。
核心原因分析
- 组件卸载重建:切换步骤时
UploadDocumentsRegistration组件会被卸载,返回时重新挂载,useForm默认值被设为空字符串,导致文件字段重置。 - 文件状态未持久化:仅从Redux恢复了复选框、地点等字段,未处理文件字段的存储与恢复。
- 浏览器安全限制:无法直接通过
defaultValue给<input type="file">设置文件对象,需通过状态同步+UI提示的方式模拟保留选中状态。
解决方案步骤
1. 将选中文件持久化到Redux
修改UploadDocumentsRegistration.jsx中的handleFileUpload,在文件验证通过后将文件对象存入Redux:
const handleFileUpload = async (name, event) => { const file = event.target.files[0]; if (!file) { if (event.target.name === 'id_photo') setProfileImage(null); // 清空Redux对应字段 dispatch(updateUploadDocumentsField({ [event.target.name]: null })); return; } switch (name) { case 'image': if (file.type === 'image/jpeg' || file.type === 'image/png') { if (file.size <= 5 * 1024 * 1024) { if (event.target.name === 'id_photo') setProfileImage(URL.createObjectURL(file)); // 保存文件到Redux dispatch(updateUploadDocumentsField({ [event.target.name]: file })); return; } else { event.target.value = ''; if (event.target.name === 'id_photo') setProfileImage(null); dispatch(updateUploadDocumentsField({ [event.target.name]: null })); alert('File must be less than 5MB.'); return; } } else { event.target.value = ''; if (event.target.name === 'id_photo') setProfileImage(null); dispatch(updateUploadDocumentsField({ [event.target.name]: null })); alert('File must be in Image format.'); return; } case 'pdf': if (file.type === 'application/pdf') { if (file.size <= 5 * 1024 * 1024) { // 保存文件到Redux dispatch(updateUploadDocumentsField({ [event.target.name]: file })); return; } else { event.target.value = ''; dispatch(updateUploadDocumentsField({ [event.target.name]: null })); alert('File must be less than 5MB.'); return; } } else { event.target.value = ''; dispatch(updateUploadDocumentsField({ [event.target.name]: null })); alert('File must be in PDF format.'); return; } default: alert('Something went wrong'); return; } };
注:需在Redux的
UploadDocumentsRegistration切片中添加updateUploadDocumentsFieldaction,用于更新单个字段值。
2. 从Redux加载文件作为表单默认值
修改UploadDocumentsRegistration.jsx的defaultValues,优先使用Redux中已保存的文件:
const { uploadDocumentsRegistration } = useSelector((state) => state?.UploadDocumentsRegistration); // 从Redux获取已保存值,无值则用默认空值 const defaultValues = { id_photo: uploadDocumentsRegistration?.id_photo || '', dob_certificate: uploadDocumentsRegistration?.dob_certificate || '', udegree_certificate: uploadDocumentsRegistration?.udegree_certificate || '', hpdegree_certificate: uploadDocumentsRegistration?.hpdegree_certificate || '', noc_certificate: uploadDocumentsRegistration?.noc_certificate || '', id_signature: uploadDocumentsRegistration?.id_signature || '', place: uploadDocumentsRegistration?.place || '', checkbox1: uploadDocumentsRegistration?.self_declaration?.checkbox1 || false, checkbox2: uploadDocumentsRegistration?.self_declaration?.checkbox2 || false, checkbox3: uploadDocumentsRegistration?.self_declaration?.checkbox3 || false, }; const methods = useForm({ resolver: yupResolver(DocumentsDetailsSchema), defaultValues, });
3. 同步文件状态并显示选中提示
由于浏览器限制无法直接设置文件输入框的value,需通过watch获取表单状态并显示已选文件名,同时在组件挂载时从Redux恢复文件状态:
修改RHFUploadFile.jsx:
import React from 'react'; import { useFormContext } from 'react-hook-form'; import ErrorMessage from './ErrorMessage'; const RHFUploadFile = ({ name, message, ...rest }) => { const { register, formState: { errors }, watch, } = useFormContext(); const selectedFile = watch(name); // 获取当前表单中的文件 return ( <> <input type="file" {...register(name)} {...rest} /> {/* 显示已选中的文件名 */} {selectedFile && <span className="selected-file">{selectedFile.name}</span>} <ErrorMessage error={errors[name]} /> {message && <label>{message}</label>} </> ); }; export default RHFUploadFile;
修改UploadDocumentsRegistration.jsx的useEffect:
useEffect(() => { if (!uploadDocumentsRegistration) return; // 恢复复选框和地点 setValue('checkbox1', uploadDocumentsRegistration?.self_declaration?.checkbox1); setValue('checkbox2', uploadDocumentsRegistration?.self_declaration?.checkbox2); setValue('checkbox3', uploadDocumentsRegistration?.self_declaration?.checkbox3); setValue('place', uploadDocumentsRegistration?.place); // 恢复文件字段 setValue('id_photo', uploadDocumentsRegistration?.id_photo); setValue('dob_certificate', uploadDocumentsRegistration?.dob_certificate); setValue('udegree_certificate', uploadDocumentsRegistration?.udegree_certificate); setValue('hpdegree_certificate', uploadDocumentsRegistration?.hpdegree_certificate); setValue('noc_certificate', uploadDocumentsRegistration?.noc_certificate); setValue('id_signature', uploadDocumentsRegistration?.id_signature); // 重新生成头像预览URL if (uploadDocumentsRegistration?.id_photo) { setProfileImage(URL.createObjectURL(uploadDocumentsRegistration.id_photo)); } }, [setValue, uploadDocumentsRegistration]);
4. 优化组件卸载行为(可选)
当前Index.js通过useEffect切换组件会导致频繁卸载重建,可改为直接条件渲染或用React.memo包装组件:
修改Index.js:
const Index = () => { return ( <> {step === 0 && <PersonalDetailsRegistration handlePrev={handlePrev} handleNext={handleNext} stepperValue={step} />} {step === 1 && <EducationDetailsRegistration handlePrev={handlePrev} handleNext={handleNext} stepperValue={step} />} {step === 2 && <WorkExperienceRegistration handlePrev={handlePrev} handleNext={handleNext} stepperValue={step} />} {step === 3 && <SkillSetRegistration handlePrev={handlePrev} handleNext={handleNext} stepperValue={step} />} {step === 4 && <UploadDocumentsRegistration handlePrev={handlePrev} handleNext={handleNext} stepperValue={step} />} {step >=5 && <NotFound />} </> ); }
或用React.memo包装UploadDocumentsRegistration:
export default React.memo(UploadDocumentsRegistration);
内容的提问来源于stack exchange,提问作者Dodi Tank
相关产品推荐
相关产品推荐

