Last active
July 7, 2021 13:50
-
-
Save Hfreitas/505d5820d26b1455d37e9ec1d0f0f413 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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