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>
)};
CodePudding user response:
If you want to do this with a ref, then you will need to remove the onl oad 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 onl oad 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}
onl oad={() => setIsLoaded(true)}
/>
</div>
);
};
CodePudding user response:
In your instance, the only time you want to use useEffect is when DOM is fully loaded, and your ref is ready. Hence you need a hook
E.g.
function useHasMounted() {
const [hasMounted, setHasMounted] = React.useState(false);
React.useEffect(() => {
setHasMounted(true);
}, []);
return hasMounted;
}
Then your Component should be corrected to be as follows
const hasMounted = useHasMounted();
useEffect(() => {
if (hasMounted) {
imageRef.current.onload = () => setIsLoaded(true);
}
}, [hasMounted]); //<-- call once when dom is loaded.
I understand you want to call onl oad whenever images is loaded, however, please do note this do not always work because images loaded from cache does not call onload
.