skip to Main Content

I am creating a React component that renders arbitrary HTML/JavaScript inside of an iframe set using srcDoc attribute. However, the iframe could have scripts that may potentially have errors and I would like to show an error message instead of the iframe in that case. However, the triggering of the error seems very inconsistent I’m speculating due to a timing issue. How do I ensure the error handler is triggered?

const RenderHtml = ({ htmlBlock }: { htmlBlock: HtmlBlock }) => {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [hasError, setHasError] = useState<boolean>(false);

  useEffect(() => {
    if (!iframeRef.current) return;
    const iframe = iframeRef.current;

    if (iframe.contentWindow) {
      iframe.contentWindow!.addEventListener("error", onIframeError);
    }

    return () => {
      if (iframe.contentWindow) {
        iframe.contentWindow!.removeEventListener("error", onIframeError);
      }
    }
  }, [htmlBlock.html]);

  const onIframeError = (event: ErrorEvent) => {
    event.preventDefault();
    setHasError(true);
  };

  return !hasError ? (
    <iframe
      ref={iframeRef}
      srcDoc={htmlBlock.html}
      style={{
        border: "none",
        width: "100%",
      }}
    ></iframe>
  ) : (
    <Alert color="danger">There was an error loading the HTML content</Alert>
  );
};

2

Answers


  1. i modified your code to use window.postMessage():

    const RenderHtml = ({ htmlBlock }: { htmlBlock: HtmlBlock }) => {
    const iframeRef = useRef<HTMLIFrameElement>(null);
    const [hasError, setHasError] = useState<boolean>(false);
    
    useEffect(() => {
    if (!iframeRef.current) return;
    const iframe = iframeRef.current;
    
    if (iframe.contentWindow) {
      iframe.contentWindow!.postMessage("ready", "*");
      window.addEventListener("message", onIframeMessage);
    }
    
    return () => {
      if (iframe.contentWindow) {
        window.removeEventListener("message", onIframeMessage);
      }
     }
     }, [htmlBlock.html]);
    
    const onIframeMessage = (event: MessageEvent) => {
    if (event.data === "error") {
      setHasError(true);
    }
    };
    
    return !hasError ? (
    <iframe
      ref={iframeRef}
      srcDoc={htmlBlock.html}
      style={{
        border: "none",
        width: "100%",
      }}
      ></iframe>
     ) : (
    <Alert color="danger">There was an error loading the HTML content</Alert>
     );
     };
    

    In this code, we are using window.postMessage() to send a message to the iframe when it is ready. We then add an event listener to the window object to listen for messages from the iframe. If the message received is “error”, we set the hasError state to true.
    see if it works

    Login or Signup to reply.
    1. Move the error handling logic inside the useEffect hook to ensure it’s properly set up when the iframe is ready.
    2. Use the load event instead of the error event to handle potential errors in the iframe’s content.

    Example:

    import React, { useEffect, useRef, useState } from "react";
    
    const RenderHtml = ({ htmlBlock }: { htmlBlock: HtmlBlock }) => {
      const iframeRef = useRef<HTMLIFrameElement>(null);
      const [hasError, setHasError] = useState<boolean>(false);
    
      useEffect(() => {
        if (!iframeRef.current) return;
    
        const iframe = iframeRef.current;
    
        const handleLoad = () => {
          // Check if the iframe's contentDocument is accessible
          // and whether it contains any error messages
          if (iframe.contentDocument?.querySelector("body script[src^='chrome-error://']")) {
            setHasError(true);
          }
        };
    
        iframe.addEventListener("load", handleLoad);
    
        return () => {
          iframe.removeEventListener("load", handleLoad);
        };
      }, [htmlBlock.html]);
    
      return !hasError ? (
        <iframe
          ref={iframeRef}
          srcDoc={htmlBlock.html}
          style={{
            border: "none",
            width: "100%",
          }}
        ></iframe>
      ) : (
        <Alert color="danger">There was an error loading the HTML content</Alert>
      );
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search