You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何保持React中<input type="file">字段值在页面跳转后不重置?

问题:页面导航返回后<input type="file">字段值重置

需要确保<input type="file">字段的值在组件变更(如页面导航离开后返回)时保持不变,但当前每次离开对应页面再返回,该字段的值都会重置。

核心原因分析

  1. 组件卸载重建:切换步骤时UploadDocumentsRegistration组件会被卸载,返回时重新挂载,useForm默认值被设为空字符串,导致文件字段重置。
  2. 文件状态未持久化:仅从Redux恢复了复选框、地点等字段,未处理文件字段的存储与恢复。
  3. 浏览器安全限制:无法直接通过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切片中添加updateUploadDocumentsField action,用于更新单个字段值。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:36:04