onError事件表现异常:图片加载成功后仍保留错误处理设置的尺寸
解决图片加载竞态导致的宽高残留问题
首先得指出你代码里的一个小问题:items.map的回调函数里没有return JSX元素,这样最终不会渲染任何图片或Logo,先把这个补上!
回到你遇到的核心问题:这是典型的图片加载竞态导致的。当源图片因为网络延迟先触发了onError,你修改了img的src和宽高,但之后源图片又加载完成了,它会替换掉你设置的factory2.svg,但宽高已经被修改且没有逻辑去恢复,所以就出现了源图成功加载却带着 fallback 尺寸的情况。
解决方案:用状态跟踪加载状态(React环境下)
我们可以通过组件状态明确区分图片的加载状态,让宽高和src完全由状态控制,彻底避免竞态问题:
// 先封装一个带 fallback 的图片组件 const FallbackImage = ({ src, fallbackSrc, defaultSize = { width: 20, height: 20 }, fallbackSize = { width: 45, height: 41 } }) => { const [loadState, setLoadState] = useState('loading'); // loading/success/error const handleLoad = () => setLoadState('success'); const handleError = () => setLoadState('error'); return ( <img src={loadState === 'error' ? fallbackSrc : src} width={loadState === 'error' ? fallbackSize.width : defaultSize.width} height={loadState === 'error' ? fallbackSize.height : defaultSize.height} onLoad={handleLoad} onError={handleError} /> ); }; // 然后在你的map里使用 items.map((item, index) => { if(item.data) { // 注意要加key,map渲染列表的必要条件 return ( <FallbackImage key={index} // 建议用item的唯一id代替index src={`${item.data}?size=20`} fallbackSrc="factory2.svg" /> ); } else { return <Logo key={index} styleName="defaultIconLogo" width={25} height={20} />; } })
为什么这个方案有效?
- 用
loadState状态明确记录图片的加载结果,不管网络请求的顺序如何,状态都会准确反映最终的加载状态 - 当源图片最终加载成功时,
onLoad会触发并把loadState设为success,此时img的宽高会自动恢复为默认的20x20,src也会保持源URL - 封装成独立组件后,复用性更强,也避免了在map里写复杂的逻辑
如果你不想封装组件,也可以直接在父组件里处理(适合简单场景)
items.map((item, index) => { if(item.data) { const [hasError, setHasError] = useState(false); return ( <img key={index} src={hasError ? 'factory2.svg' : `${item.data}?size=20`} width={hasError ? 45 : 20} height={hasError ? 41 : 20} onLoad={() => setHasError(false)} onError={() => setHasError(true)} /> ); } else { return <Logo key={index} styleName="defaultIconLogo" width={25} height={20} />; } })
这个方案的核心思路和组件化版本一致,都是用状态来控制显示逻辑,彻底解决竞态问题。
内容的提问来源于stack exchange,提问作者Norayr Ghukasyan
相关产品推荐
相关产品推荐

