Skip to content

Instantly share code, notes, and snippets.

@Hfreitas
Last active July 7, 2021 13:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hfreitas/505d5820d26b1455d37e9ec1d0f0f413 to your computer and use it in GitHub Desktop.
Save Hfreitas/505d5820d26b1455d37e9ec1d0f0f413 to your computer and use it in GitHub Desktop.
import { useEffect, useRef } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { UnregisterCallback } from 'history';
/**
* Hook auxiliar. Referência para implementação:
* - https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state;
* - https://usehooks.com/usePrevious/;
*/
const useCurrentPath = (value: string): string | null => {
const locationRef = useRef<string | null>(null);
useEffect(() => {
locationRef.current = value;
}, [value]);
return locationRef.current;
};
/**
* Este hook customizado possui duas funções:
*
* 1. adicionar um event listener ao evento beforeUnload e
* acionar o prompt padrão do navegador. Referências para implementação:
* - https://developer.mozilla.org/pt-BR/docs/Web/API/Window/beforeunload_event
* - https://dev.to/eons/detect-page-refresh-tab-close-and-route-change-with-react-router-v5-3pd
*
* 2. adicionar confirmação de navegação, quando uma condição pré determinada for verdadeira. Referências
* para implementação:
*
* - https://medium.com/@jozollo/blocking-navigation-with-react-router-v4-a3f2e359d096;
* - https://github.com/ReactTraining/react-router/issues/5405;
* - https://gist.github.com/sibelius/60a4e11da1f826b8d60dc3975a1ac805 (mencionado na issue acima);
*
* Importante: O uso do hook foi escolhido, pois foi a melhor forma de resolver o problema
* de alteração da url do navegador, mesmo quando o usuário cancela a navegação. Este problema é
* citado na issue referida acima.
*
* @param {boolean} when - Condição de disparo do prompt para impedir navegação
* @param {string} message - Messagem disparada para o usuário quando a navegação ocorre via React Router
*/
export default (
when = false,
message = 'As alterações que você fez talvez não sejam salvas.',
): void => {
const history = useHistory();
const { pathname } = useLocation();
const previousLocation = useCurrentPath(pathname);
const URLRef = useRef<UnregisterCallback | null>(null);
const onUnload = (event: BeforeUnloadEvent): string => {
// garantindo compatibilidade com browser antigos e mobile
const listener = event || window.event;
listener.preventDefault();
if (listener) {
/* compatibilidade com browser engines Gecko (Firefox),
Trident(Internet Explorer) e Chrome versões 34+ */
listener.returnValue = '';
}
/* compatibilidade com browser engines Gecko (Firefox) versões anteriores
a 4, Webkit(browsers iOS, Safari, Chrome <= 27) e Chrome versões <34 */
return '';
};
useEffect(() => {
if (when) {
URLRef.current = history.block((location, action) => {
const isLocationChanged = previousLocation !== location.pathname;
if (isLocationChanged) {
const confirm = window.confirm(message);
if (!confirm && action === 'POP') {
// caso a navegação seja cancelada, recuperando valor da url anterior
window.history.replaceState(null, '', `/#${previousLocation}`);
}
if (confirm) {
// navegação permitida
return undefined;
}
}
return false;
});
window.addEventListener('beforeunload', onUnload);
} else {
URLRef.current = null;
}
return () => {
if (URLRef.current) {
/* cancelando a subscrição e parando o bloqueio de mudança de rota
e listener do objeto history.
Para isso, é necessário chamar a função retornada por history.block e history.listen.
Referências:
- https://github.com/ReactTraining/history/blob/v4/docs/Blocking.md;
- https://github.com/ReactTraining/history/blob/v4/docs/GettingStarted.md#listening */
URLRef.current();
URLRef.current = null;
window.removeEventListener('beforeunload', onUnload);
}
};
}, [message, when, history, previousLocation]);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment