React中AWS S3预签名URL作为img src无法正常显示问题
问题原因
- 你在map遍历中发起的获取预签名URL的fetch是异步操作,不会阻塞后续代码执行。所有fetch请求还没返回结果时,你就直接把未携带image属性的newMemes存入了state,此时渲染的img标签src为undefined,自然会触发加载失败显示缺省图。
- 后续fetch请求成功后你直接修改原meme对象的image属性,不符合React状态不可变的更新规则,React无法检测到对象属性的变更,不会触发组件重渲染,所以页面不会更新为正确的图片地址。
- 你看到控制台打印的newMemes有image属性,是因为浏览器控制台打印引用类型对象时会懒加载,你展开的时候异步请求已经完成,对象已经被修改,而你同步打印image属性时异步还没返回,所以输出undefined,完全符合你给出的日志表现。
- 单条数据的组件能正常运行,是因为你在图片请求成功之后主动调用了setState触发重渲染,和列表页的逻辑本质不同。
修复方案
使用Promise.all等待所有图片预签名URL请求全部完成后,再统一更新state,修改后的useEffect代码如下:
useEffect(() => { fetch(`${process.env.REACT_APP_BACKEND_URL}/memes/`) .then((response) => response.json()) .then(async (memes) => { // 等待所有图片预签名URL请求完成 const newMemes = await Promise.all( memes.map(async (meme) => { const imageRes = await fetch(`${process.env.REACT_APP_BACKEND_URL}/images/${meme.file}`); const image = await imageRes.json(); // 返回新对象,符合React状态不可变原则 return { ...meme, image }; }) ); // 所有数据准备完成后再更新state setState({ memes: newMemes }); }) .catch(err => console.error('数据加载失败:', err)); }, []);
如果需要优化加载体验,先渲染列表基础信息再逐步更新图片,也可以单条请求完成后就更新state数组对应位置的对象,只要保证每次更新都通过setState传入新的数组副本即可。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

