I have this component, so I want to clean up in useEffect. I googled the issue and there is no helpful information.
const LoadableImg = ({src, alt}) => {
const [isLoaded, setIsLoaded] = useState(false);
let imageRef = useRef(null);
useEffect(() => {
if (isLoaded) return;
if (imageRef.current) {
imageRef.current.onload = () => setIsLoaded(true);
}
return () => {
imageRef.current = null;
};
}, [isLoaded]);
return (
<div className={isLoaded ? 'l_container_loaded' : 'l_container'}>
<img ref={imageRef} className={isLoaded ? "l_image_loaded" : 'l_image'}
src={src}
alt={alt}/>
</div>
) };
I can't figure out how to clean up in useEffect.
UPDATE added another useEffect, according to Arcanus answer.
const LoadableImg = ({src, alt}) => {
const [isLoaded, setIsLoaded] = useState(false);
let imageRef = useRef(null);
useEffect(() => {
if (isLoaded) return;
if (imageRef.current) {
imageRef.current.onload = () => setIsLoaded(true);
}
}, [isLoaded]);
useEffect(() => {
return () => {
imageRef.current = null;
};
},[])
return (
<div className={isLoaded ? 'l_container_loaded' : 'l_container'}>
<img ref={imageRef} className={isLoaded ? "l_image_loaded" : 'l_image'}
src={src}
alt={alt}/>
</div>
)};
If you want to do this with a ref, then you will need to remove the onload function, but you do not need to null out imageRef.current
:
useEffect(() => {
if (isLoaded) return;
const element = imageRef.current;
if (element) {
element.onload = () => setIsLoaded(true);
return () => {
element.onload = null;
}
}
}, [isLoaded]);
That said, i recommend you do not use a ref for this. A standard onLoad prop will work just as well, without the need for all the extra logic for adding and removing the event listener:
const LoadableImg = ({ src, alt }) => {
const [isLoaded, setIsLoaded] = useState(false);
return (
<div className={isLoaded ? "l_container_loaded" : "l_container"}>
<img
className={isLoaded ? "l_image_loaded" : "l_image"}
src={src}
alt={alt}
onLoad={() => setIsLoaded(true)}
/>
</div>
);
};