Interval declared in useEffect not being cleared

  Kiến thức lập trình

An Interval is declared in a useEffect. This interval goes ahead and conditionally sets the src property on an iframe, referenced by useRef. The goal is to reload the iframe, if it hasn’t been loaded yet.

import { useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
import { getRemoteUrl } from "../utils/getRemoteUrl";
import { mapUrl } from "../utils/mapUrl";

interface Props {
  setUrl: React.Dispatch<React.SetStateAction<string>>;
}

const PdfViewer = (props: Props) => {
  const ref = useRef<HTMLIFrameElement>(null);
  const { filename, encoded } = useParams();
  const [loaded, setLoaded] = useState(false);
  const intervalId = useRef<NodeJS.Timer | null>(null);

  useEffect(() => {
    if (encoded && filename) {
      const decodedUrl = decodeURIComponent(encoded);
      const mappedUrl = mapUrl(decodedUrl);
      props.setUrl(mappedUrl);

      const localUrl = `${mappedUrl}/${encodeURIComponent(filename)}`;
      const iframeUrl = `https://docs.google.com/gview?url=${getRemoteUrl(
        localUrl
      )}&embedded=true`;

      const iframe = ref.current;
      if (iframe) {
        iframe.src = iframeUrl;
        if (intervalId.current) clearInterval(intervalId.current);
        intervalId.current = setInterval(() => {
          if (!loaded) {
            iframe.src = iframeUrl;
          } else if (intervalId.current) {
            clearInterval(intervalId.current);
          }
        }, 5000);
      }

      return () => {
        if (intervalId.current) {
          clearInterval(intervalId.current);
        }
      };
    }
  }, [filename, encoded, props.setUrl, loaded]);

  if (!filename) {
    return <h1>No file found</h1>;
  }

  return (
    <div className="flex flex-col">
      <p className="text-center">
        This might take a while. Try refreshing the page periodically.
        <br></br>If the file is not loading, try{" "}
        <a
          className="underline"
          href={getRemoteUrl(
            mapUrl(decodeURIComponent(encoded!)) +
              `/${encodeURIComponent(filename)}`
          )}
        >
          downloading it.
        </a>
      </p>
      <iframe
        ref={ref}
        className="w-full h-[80vh]"
        title="pdf"
        onLoad={() => {
          setLoaded(true);
        }}
      ></iframe>
    </div>
  );
};

export default PdfViewer;

Expected: When the iframe loads, “loaded” becomes true and thus no further request to docs.google.com will be made.

Reality: No matter what, every 5 seconds a request is made to docs.google.com sometimes returning as a 200 and refreshing the iframe, making the app unusable.

2

Interval declared in useEffect not being cleared

An Interval is declared in a useEffect. This interval goes ahead and conditionally sets the src property on an iframe, referenced by useRef. The goal is to reload the iframe, if it hasn’t been loaded yet.

import { useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
import { getRemoteUrl } from "../utils/getRemoteUrl";
import { mapUrl } from "../utils/mapUrl";

interface Props {
  setUrl: React.Dispatch<React.SetStateAction<string>>;
}

const PdfViewer = (props: Props) => {
  const ref = useRef<HTMLIFrameElement>(null);
  const { filename, encoded } = useParams();
  const [loaded, setLoaded] = useState(false);
  const intervalId = useRef<NodeJS.Timer | null>(null);

  useEffect(() => {
    if (encoded && filename) {
      const decodedUrl = decodeURIComponent(encoded);
      const mappedUrl = mapUrl(decodedUrl);
      props.setUrl(mappedUrl);

      const localUrl = `${mappedUrl}/${encodeURIComponent(filename)}`;
      const iframeUrl = `https://docs.google.com/gview?url=${getRemoteUrl(
        localUrl
      )}&embedded=true`;

      const iframe = ref.current;
      if (iframe) {
        iframe.src = iframeUrl;
        if (intervalId.current) clearInterval(intervalId.current);
        intervalId.current = setInterval(() => {
          if (!loaded) {
            iframe.src = iframeUrl;
          } else if (intervalId.current) {
            clearInterval(intervalId.current);
          }
        }, 5000);
      }

      return () => {
        if (intervalId.current) {
          clearInterval(intervalId.current);
        }
      };
    }
  }, [filename, encoded, props.setUrl, loaded]);

  if (!filename) {
    return <h1>No file found</h1>;
  }

  return (
    <div className="flex flex-col">
      <p className="text-center">
        This might take a while. Try refreshing the page periodically.
        <br></br>If the file is not loading, try{" "}
        <a
          className="underline"
          href={getRemoteUrl(
            mapUrl(decodeURIComponent(encoded!)) +
              `/${encodeURIComponent(filename)}`
          )}
        >
          downloading it.
        </a>
      </p>
      <iframe
        ref={ref}
        className="w-full h-[80vh]"
        title="pdf"
        onLoad={() => {
          setLoaded(true);
        }}
      ></iframe>
    </div>
  );
};

export default PdfViewer;

Expected: When the iframe loads, “loaded” becomes true and thus no further request to docs.google.com will be made.

Reality: No matter what, every 5 seconds a request is made to docs.google.com sometimes returning as a 200 and refreshing the iframe, making the app unusable.

2

LEAVE A COMMENT