Renders an image that supports lazy loading.
- Use the
useState()
hook to create a stateful value that indicates if the image has been loaded. - Use the
useEffect()
hook to check if theHTMLImageElement.prototype
contains'loading'
, effectively checking if lazy loading is supported natively. If not, create a newIntersectionObserver
and useIntersectionObserver.observer()
to observer the<img>
element. Use thereturn
value of the hook to clean up when the component unmounts. - Use the
useCallback()
hook to memoize a callback function for theIntersectionObserver
. This callback will update theisLoaded
state variable and useIntersectionObserver.disconnect()
to disconnect theIntersectionObserver
instance. - Use the
useRef()
hook to create two refs. One will hold the<img>
element and the other theIntersectionObserver
instance, if necessary. - Finally, render the
<img>
element with the given attributes. Applyloading='lazy'
to make it load lazily, if necessary. UseisLoaded
to determine the value of thesrc
attribute.
代码实现
const LazyLoadImage = ({
alt,
src,
className,
loadInitially = false,
observerOptions = { root: null, rootMargin: '200px 0px' },
...props
}) => {
const observerRef = React.useRef(null);
const imgRef = React.useRef(null);
const [isLoaded, setIsLoaded] = React.useState(loadInitially);
const observerCallback = React.useCallback(
entries => {
if (entries[0].isIntersecting) {
observerRef.current.disconnect();
setIsLoaded(true);
}
},
[observerRef]
);
React.useEffect(() => {
if (loadInitially) return;
if ('loading' in HTMLImageElement.prototype) {
setIsLoaded(true);
return;
}
observerRef.current = new IntersectionObserver(
observerCallback,
observerOptions
);
observerRef.current.observe(imgRef.current);
return () => {
observerRef.current.disconnect();
};
}, []);
return (
<img
alt={alt}
src={isLoaded ? src : ''}
ref={imgRef}
className={className}
loading={loadInitially ? undefined : 'lazy'}
{...props}
/>
);
};
使用样例
ReactDOM.createRoot(document.getElementById('root')).render(
<LazyLoadImage
src="https://picsum.photos/id/1080/600/600"
alt="Strawberries"
/>
);