먼저 예시코드부터 보자.
// useOnBeforeUnload.ts
export const useOnBeforeUnload = () => {
const router = useRouter();
const [formChanged, setFormChanged] = useState<boolean>(false);
useEffect(() => {
const warningText =
'저장되지않은 내용이 있습니다. 계속 진행하시겠습니까?';
const handleWindowClose = (e: BeforeUnloadEvent) => {
if (!unsavedChanges) return;
e.preventDefault();
return (e.returnValue = warningText);
};
const handleBrowseAway = () => {
if (!unsavedChanges) return;
if (window.confirm(warningText)) return;
router.events.emit('routeChangeError');
if (router.asPath !== window.location.pathname) {
window.history.pushState('', '', router.asPath);
}
throw '페이지 이동이 취소되었습니다.';
};
window.addEventListener('beforeunload', handleWindowClose);
router.events.on('routeChangeStart', handleBrowseAway);
return () => {
window.removeEventListener('before unload', handleWindowClose);
router.events.off('routeChangeStart', handleBrowseAway);
};
}, [formChanged]);
return {formChanged, setFormChanged}
}
처음 필자 생각에는 아래사람의 예시와 같이 window.history.pushState('', '', router.asPath) 부분이 없어도 정상작동할줄 알았다.
const handleBrowseAway = () => {
if (!unsavedChanges) return;
if (window.confirm(warningText)) return;
router.events.emit('routeChangeError');
throw '페이지 이동이 취소되었습니다.';
};
문제는 위의 코드는 nextjs의 component route는 막지만, path의 이동은 막지못하여 후처리를 해줘야했다.
const handleBrowseAway = () => {
if (!unsavedChanges) return;
if (window.confirm(warningText)) return;
router.events.emit('routeChangeError');
if (router.asPath !== window.location.pathname) {
window.history.pushState('', '', router.asPath);
}
throw '페이지 이동이 취소되었습니다.';
};
아래와 같이 window, router에 모두 event를 등록하는것은 routeChangeStart은 route이동. 즉, 뒤로가거나 다른페이지로의 이동은 핸들링가능하나, 페이지 새로고침같은 이벤트는 핸들링 안되기 때문.
window.addEventListener('beforeunload', handleWindowClose);
router.events.on('routeChangeStart', handleBrowseAway);
참고 링크 :
https://nextjs.org/docs/api-reference/next/router
https://github.com/vercel/next.js/issues/2694
React만 이용하고서는 동일한 기능을 구현할수 없을까요?
nextjs를 사용하고 있지 않는데 구현이 쉽지 않네요 ㅠ