Home > Blockchain >  How to cleanup useRef in useEffect?
How to cleanup useRef in useEffect?

Time:02-24

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.

  • Related