'React-router-dom Prompt: Iframe within a page gets unloaded/loaded [closed]

I am working on a react spa to show alert on unsaved form changes. I am using [email protected] Prompt Component for this use-case.

The page which has the form also has an iframe. The problem is when the user fills in the form but doesn't save it and tries to navigate using the browser-back button, the alert is shown and if the user cancels the navigation, the iframe unload/load fires even though the page itself didn't unmount/mount. This leads to the iframe losing its state



Solution 1:[1]

I use Prompt from react-router-dom combined with a state on onFocus function

import React, { useRef, useState, Fragment } from "react";
import { Prompt } from "react-router-dom";

const QuoteForm = (props) => {
   const textInputRef = useRef();

   // FOCUSED FORM STATE
   const [isEntering, setIsEntering] = useState(false);

   function submitFormHandler(event) {
      event.preventDefault();

      const enteredText = textInputRef.current.value;
   }
   // LEAVING THE FORM PROMPT
   const doneEnteringHandler = () => {
      setIsEntering(false);
   };
   const focusedForm = () => {
      setIsEntering(true);
   };

   return (
      <Fragment>
         <Prompt when={isEntering} message={(location) => "All data will be lost."} />
         <Card>
            <form
               onFocus={focusedForm}
               className={classes.form}
               onSubmit={submitFormHandler}
            >
               <div>
                  <label htmlFor="text">Text</label>
                  <textarea id="text" rows="5" ref={textInputRef}></textarea>
               </div>
               <div>
                  <button onClick={doneEnteringHandler} className="btn">Add Quote</button>
               </div>
            </form>
         </Card>
      </Fragment>
   );
};

export default QuoteForm;

Solution 2:[2]

This answer is for react-router-dom v6

The working solution is put onto the Github repo.

Have a glance on the main code, src/pages/Demo.tsx:

import Iframe from "react-iframe";
import React from "react";
import {usePrompt} from "../hooks/usePromptBlocker";

const Demo = () => {
    usePrompt(
        "Hello from usePrompt -- Are you sure you want to leave?",
        true
    );

    return (
        <Iframe url={'http://127.0.0.1:5500/demo-form.html'} width="375" height="667"/>
    );
}

export default Demo;

Sometimes we will rollback the version of react-router-dom to v5 and not to use react-router-dom v6 because usePrompt and useBlocker are gone.

We can definitely use v6 now, as a workaround has been introduced here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Blind2k
Solution 2