React ImageStrip组件状态变更时如何避免图片重新加载?
问题解决:React图片条点击时图片重复加载
问题原因
你的代码里,urls = urlsCallback()会在App组件每次渲染时执行——比如点击图片触发setSelected更新状态的时候,都会重新生成一组带新随机数的图片URL。这就导致所有<img>的src属性被替换,浏览器自然会重新请求新图片。
哪怕urlsCallback用了useCallback缓存,但你每次渲染都调用它生成新数组,urls的引用还是会变,传给ImageStrip后会触发子组件重渲染,且图片URL完全更新。
解决方案:固定图片URL数组
我们要确保图片URL数组只在组件初始化时生成一次,后续渲染不再变化,有两种简单实现方式:
方案1:用useMemo缓存URL数组
把URL数组的生成用useMemo包裹,依赖项设为空数组,确保只执行一次:
import * as React from 'react'; import ImageStrip from './ImageStrip'; function App() { const [selected, setSelected] = React.useState(''); function handleClick(url: string) { setSelected(url); } // 用useMemo缓存,仅组件挂载时生成一次URL数组 const urls = React.useMemo(() => { const randomNumbers = Array.from({ length: 5 }, () => Math.floor(Math.random() * 100) ).map((m) => Number(m)); return randomNumbers.map((m) => `https://picsum.photos/100?random=${m}`); }, []); return ( <> <div> <p>selected url: {selected}</p> </div> <div> <ImageStrip urls={urls} selectedUrl={selected} onClick={handleClick} /> </div> </> ); } export default App;
方案2:在useState中初始化URL数组
直接把URL数组作为useState的初始值,同样只在组件挂载时生成一次:
import * as React from 'react'; import ImageStrip from './ImageStrip'; function App() { // 初始化时生成URL数组,后续不再改变 const [urls] = React.useState(() => { const randomNumbers = Array.from({ length: 5 }, () => Math.floor(Math.random() * 100) ).map((m) => Number(m)); return randomNumbers.map((m) => `https://picsum.photos/100?random=${m}`); }); const [selected, setSelected] = React.useState(''); function handleClick(url: string) { setSelected(url); } return ( <> <div> <p>selected url: {selected}</p> </div> <div> <ImageStrip urls={urls} selectedUrl={selected} onClick={handleClick} /> </div> </> ); } export default App;
额外优化:减少ImageStrip不必要渲染
如果ImageStrip组件没做性能优化,哪怕urls不变,selected变化时它还是会重渲染。可以给ImageStrip套个React.memo,避免无意义的重渲染:
// ImageStrip.tsx import React from 'react'; type Props = { urls: string[]; selectedUrl: string; onClick: (url: string) => void; }; // 用React.memo包裹,仅当props变化时才重渲染 const ImageStrip = React.memo(({ urls, selectedUrl, onClick }: Props) => { return ( <div style={{ display: 'flex', gap: '8px' }}> {urls.map((url) => ( <img key={url} src={url} onClick={() => onClick(url)} style={{ border: selectedUrl === url ? '2px solid blue' : '2px solid transparent', borderRadius: '4px' }} alt="strip item" /> ))} </div> ); }); export default ImageStrip;
这么处理后,点击图片时只有选中状态变化,图片URL不会重新生成,自然也就不会触发图片重复加载了。
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

