React中多图片Base64存入State后无法渲染问题排查
问题:图片转Base64存入状态后渲染无效果,数组长度始终为0?
我尝试将多张图片文件转换为Base64格式存入数组状态中,代码如下:
const uploadImage = e => { if (e.target.files && e.target.files.length > 0) { // ... let imagesArray = []; for (let i = 0; i < e.target.files.length; i++) { const image = e.target.files[i]; const verified = verifyImage(image); if (verified) { const reader = new FileReader(); const type = image.type; reader.addEventListener('load', () => { let imgObj = { base64: reader.result, name: image.name, type, size: image.size, }; imagesArray.push(imgObj) }); reader.readAsDataURL(image); } } setImages(imagesArray); } e.target.value = null; }
更新状态后,使用map函数渲染时没有效果:
{ images.length > 0 ? images.map((imageObj, i) => { return ( <div key={i}> <img src={imageObj.base64} alt='' /> <div> <span>{imageObj.size ? imageObj.size : '-'}</span> <span>{imageObj.name ? imageObj.name : '-'}</span> <span onClick={() => removeImage(i)} > {translations(locale).remove} </span> </div> </div> ) }) : null }
状态数组已更新,但array.length仍显示为0,请问我哪里出错了?
解决方案
这个问题的核心在于FileReader的load事件是异步操作,你现在的代码逻辑有个时序问题:
当你在循环里调用reader.readAsDataURL(image)时,浏览器会在后台读取文件,这个过程是异步的,不会阻塞后续代码执行。所以当循环结束后立即调用setImages(imagesArray)时,所有的load回调函数都还没执行,imagesArray此时还是空数组,自然渲染的时候images.length为0。
这里有两种靠谱的解决方式:
方法一:使用Promise.all统一处理异步操作
把每个文件的读取过程包装成Promise,然后用Promise.all等待所有读取完成后再更新状态:
const uploadImage = e => { if (e.target.files && e.target.files.length > 0) { const filePromises = []; for (let i = 0; i < e.target.files.length; i++) { const image = e.target.files[i]; const verified = verifyImage(image); if (verified) { const promise = new Promise((resolve) => { const reader = new FileReader(); const type = image.type; reader.addEventListener('load', () => { resolve({ base64: reader.result, name: image.name, type, size: image.size, }); }); reader.readAsDataURL(image); }); filePromises.push(promise); } } // 等待所有文件读取完成 Promise.all(filePromises).then(imagesArray => { setImages(imagesArray); }); } e.target.value = null; }
方法二:在每个load回调中更新状态(注意函数式更新)
如果你不想用Promise,也可以在每个load回调里更新状态,但要使用React的函数式状态更新,确保每次更新都是基于最新的状态:
const uploadImage = e => { if (e.target.files && e.target.files.length > 0) { for (let i = 0; i < e.target.files.length; i++) { const image = e.target.files[i]; const verified = verifyImage(image); if (verified) { const reader = new FileReader(); const type = image.type; reader.addEventListener('load', () => { const imgObj = { base64: reader.result, name: image.name, type, size: image.size, }; // 使用函数式更新,确保拿到最新的images状态 setImages(prevImages => [...prevImages, imgObj]); }); reader.readAsDataURL(image); } } } e.target.value = null; }
这两种方法都能解决你的问题,第一种适合需要等待所有文件都处理完再一次性更新状态的场景,第二种适合每读取完一个文件就立即更新状态、实时显示的场景,你可以根据自己的需求选择。
内容的提问来源于stack exchange,提问作者user13449589
相关产品推荐
相关产品推荐

