Skip to content

Instantly share code, notes, and snippets.

@sibelius
Last active October 27, 2022 19:05
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sibelius/60a4e11da1f826b8d60dc3975a1ac805 to your computer and use it in GitHub Desktop.
Save sibelius/60a4e11da1f826b8d60dc3975a1ac805 to your computer and use it in GitHub Desktop.
Prompt user before leaving route or reload
import { useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
export const usePrompt = (when: boolean, message: string = 'Are you sure you want to quit without saving your changes?') => {
const history = useHistory();
const self = useRef(null);
const onWindowOrTabClose = event => {
if (!when) {
return;
}
if (typeof event == 'undefined') {
event = window.event;
}
if (event) {
event.returnValue = message;
}
return message;
};
useEffect(() => {
if (when) {
self.current = history.block(message);
} else {
self.current = null;
}
window.addEventListener('beforeunload', onWindowOrTabClose);
return () => {
if (self.current) {
self.current();
self.current = null;
}
window.removeEventListener('beforeunload', onWindowOrTabClose);
}
}, [message, when]);
};
@kirkdrichardson
Copy link

Looks good, however this hook violates a dependency guideline recommended in the React docs on the useEffect hook:

If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders. Learn more about how to deal with functions and what to do when the array values change too often.
...
We recommend using the exhaustive-deps rule as part of our eslint-plugin-react-hooks package. It warns when dependencies are specified incorrectly and suggests a fix.

Adding history to the useEffect dependency array and moving the onWindowOrTabClose definition inside the function passed to useEffect resolves the issue.

@meirkl
Copy link

meirkl commented Apr 24, 2020

Thanks for this awesome hook.
I'm having some problems with showing propmpt on reload... any idea why?
I needed to add some types for it to work for me.

import { useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import { UnregisterCallback } from 'history';

export const usePrompt = (
  when: boolean,
  message: string = 'Are you sure you want to quit without saving your changes?',
) => {
  const history = useHistory();

  const self = useRef<UnregisterCallback | null>();

  const onWindowOrTabClose = (event: BeforeUnloadEvent) => {
    if (!when) {
      return;
    }

    if (typeof event === 'undefined') {
      event = window.event as BeforeUnloadEvent;
    }

    if (event) {
      event.returnValue = message;
    }

    return message;
  };

  useEffect(() => {
    if (when) {
      self.current = history.block(message);
    } else {
      self.current = null;
    }

    window.addEventListener('beforeunload', onWindowOrTabClose);

    return () => {
      if (self.current) {
        self.current();
        self.current = null;
      }

      window.removeEventListener('beforeunload', onWindowOrTabClose);
    };
  }, [message, when, onWindowOrTabClose]);
};

@sibelius
Copy link
Author

Check listeners and history.block call

@mikews93
Copy link

mikews93 commented Oct 23, 2020

It works, but when the user click on back button it shows a different window with no confirmation and let the use go back without confirm (this is on chrome)

@sibelius
Copy link
Author

const App = () => {
   const formik = useFormik();

   usePrompt(formik.dirty, t('Are you sure you want to quit without saving your changes?'));
}

@Hfreitas
Copy link

Hi, thanks for this hook. Saved my life at work!

I'm using a mod version: https://gist.github.com/Hfreitas/505d5820d26b1455d37e9ec1d0f0f413

Please, give me feedbacks.

@raghav-zwt
Copy link

Any update for react-router v6.4.1?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment