弹窗中img2无法存入MongoDB的问题求助
问题解决:弹窗中上传的图片无法存入MongoDB
问题场景
开发商品添加页面时,需上传4张图片至MongoDB,其中img2通过弹窗组件上传,其余3张图片可成功存入,但弹窗中的img2无法保存至MongoDB。
代码片段
页面组件代码
<div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> <div className="sm:col-span-3"> <label htmlFor="img1" className="block text-sm font-medium leading-6 text-gray-900 font-bold" > Image1* (Thumbnail) </label> <div className="mt-2"> <div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-orange-600 "> <input type="file" {...register("img1", { required: "name is required", })} id="img1" className="block flex-1 border-0 bg-transparent p-2 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" accept="image/*" onChange={(e) => handleImageChange(e, 1)} /> </div> </div> </div> <div className="relative"> <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={openPopup} > Open Popup </button> {isPopupOpen && ( <div className="fixed inset-0 z-50 flex items-center justify-center overflow-auto bg-gray-800 bg-opacity-75"> <div className="bg-white rounded-lg p-8"> <label htmlFor="img2" className="block text-sm font-medium leading-6 text-gray-900 font-bold" > Image2* </label> <h2 className="text-xl font-semibold mb-4">Upload Image</h2> <input type="file" {...register('img2', { required: 'name is required', })} id="img2" className="block w-full border-gray-400 p-2 rounded" accept="image/*" onChange={(e) => handleImageChange(e, 2)} /> <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={closePopup} > Close </button> </div> </div> )} </div> </div> <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> <div className="sm:col-span-3"> <label htmlFor="img3" className="block text-sm font-medium leading-6 text-gray-900 font-bold" > Image3* </label> <div className="mt-2"> <div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-orange-600 "> <input type="file" {...register("img3", { required: "name is required", })} id="img3" className="block flex-1 border-0 bg-transparent p-2 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" accept="image/*" onChange={(e) => handleImageChange(e, 3)} /> </div> </div> </div> <div className="sm:col-span-3"> <label htmlFor="img4" className="block text-sm font-medium leading-6 text-gray-900 font-bold" > Image4* </label> <div className="mt-2"> <div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-orange-600 "> <input type="file" {...register("img4", { required: "name is required", })} id="img4" className="block flex-1 border-0 bg-transparent p-2 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" accept="image/*" onChange={(e) => handleImageChange(e, 4)} /> </div> </div> </div> </div>
逻辑代码
const handleImageChange = (e, index) => { const file = e.target.files[0]; console.log(file); if (file) { const imageUrl = URL.createObjectURL(file); console.log("imageUrl", imageUrl); setImages(`img${index}`, imageUrl); } }; const openPopup = () => { setIsPopupOpen(true); }; const closePopup = () => { setIsPopupOpen(false); }; // Add images to FormData for (let i = 1; i <= 4; i++) { const fileInput = document.getElementById(`img${i}`); const file = fileInput?.files[0]; if (file) { formData.append(`image`, file); } }
问题原因
弹窗通过isPopupOpen && ...进行条件渲染,关闭弹窗(执行setIsPopupOpen(false))时,弹窗内的img2输入框组件会被卸载,DOM中不再存在该元素。此时执行document.getElementById('img2')会返回null,无法获取选中的文件,导致无法存入MongoDB。
解决方案
放弃依赖DOM获取文件对象,改为在用户选择文件时,直接将文件对象保存到React状态中,提交时从状态读取文件即可。
修改步骤
- 新增状态存储文件对象
// 初始化状态,用于存储各图片的文件对象 const [imageFiles, setImageFiles] = useState({});
- 更新handleImageChange函数
在保存预览URL的同时,将文件对象存入状态:
const handleImageChange = (e, index) => { const file = e.target.files[0]; if (file) { const imageUrl = URL.createObjectURL(file); // 保存预览URL(保持原有逻辑,若原setImages是对象状态需用函数式更新) setImages(prev => ({ ...prev, [`img${index}`]: imageUrl })); // 保存文件对象到状态 setImageFiles(prev => ({ ...prev, [`img${index}`]: file })); } };
- 修改FormData构建逻辑
从状态中直接读取文件,不再操作DOM:
// Add images to FormData for (let i = 1; i <= 4; i++) { const file = imageFiles[`img${i}`]; if (file) { formData.append(`image`, file); } }
额外优化建议
如果需要弹窗关闭后仍保留已选文件,也可以用CSS的display: none隐藏弹窗而非卸载组件,但状态存储的方式更符合React设计理念,优先推荐。
内容的提问来源于stack exchange,提问作者hussain ashraf
相关产品推荐
相关产品推荐

