포스트 작성 페이지같은 곳에서 페이지를 벗어날 때 작성 중인 내용을 잃지 않도록 페이지를 나가시겠습니까?
와 같은 내용의 확인창을 띄우는 방식의 1차 안전장치가 필요할 때가 있다. 그리고 나아가 페이지를 나가게 되더라도 특정 상황(제출 버튼을 누를 때라든지)에서는 해당 알림이 뜨지 않게 제어하고 싶은 경우가 있을 것이다.
Javascript의 window.onBeforeUnload와 같은 이벤트도 있고, react-router-dom에서 제공하는 Prompt api를 이용하는 방법도 있지만 나는 기존 코드를 작성해주신 팀원분이 next.js에서 제공하는 routeChangeStart를 사용해 해당 기능을 만들어주셨는데 제출 버튼을 누를 때도 이벤트가 발생하고 있어 이를 이용하여 코드를 수정해보았다.
next.js Router Event
Router 객체에는 라우팅 내부에서 발생하는 다양한 이벤트에 리스너를 등록할 수 있다. 지원되는 이벤트의 종류는 다음과 같다.
- routeChangeStart(url, { shallow }) - 경로가 변경되기 시작할 때 발생
- routeChangeComplete(url, { shallow }) - 경로가 완전히 변경되면 발생
- routeChangeError(err, url, { shallow }) - 경로 변경 시 오류가 발생하거나 경로 로드가 취소되면 발생
- err.cancelled - 탐색이 취소되었는지 여부를 나타냄
- beforeHistoryChange(url, { shallow }) - 브라우저의 기록을 변경하기 전에 실행
- hashChangeStart(url, { shallow }) - 해시는 변경되지만 페이지는 변경되지 않을 때 발생
- hashChangeComplete(url, { shallow }) - 해시가 변경되었지만 페이지가 변경되지 않은 경우 발생
const [toUrl, setToUrl] = useState('');
const [confirmed, setConfirmed] = useState(false);
const [RunModal, toggleModal] = useModal(ConfirmModal, {
onConfirm: () => setConfirmed(true),
content: '변경내용이 사라지게 됩니다. 페이지를 나가시겠습니까?',
});
const routeChangeStart = useCallback(
(url: string) => {
if (router.asPath.split('?')[0] !== url.split('?')[0] && !confirmed) {
setToUrl(url);
toggleModal();
router.events.emit('routeChangeError');
throw 'Abort route change. Please ignore this error.';
}
},
[confirmed, router.asPath, router.events, toggleModal]
);
useEffect(() => {
router.events.on('routeChangeStart', routeChangeStart);
return () => {
router.events.off('routeChangeStart', routeChangeStart);
};
}, [routeChangeStart, router.events]);
useEffect(() => {
if (confirmed) {
toggleModal();
router.replace(toUrl);
}
}, [toUrl, confirmed]);
const onFinalSubmit = useCallback(
e => {
if (e.target.className === 'submit css-1o39ywg') {
router.events.off('routeChangeStart', routeChangeStart);
}
setContent(EditorRef.current?.getInstance().getHtml() as string);
temporarySave(EditorRef.current?.getInstance().getHtml() as string);
},
[routeChangeStart, router.events, setContent, temporarySave]
);
/*생략*/
<SquareBtn className="submit" onClick={onFinalSubmit}>제출</SquareBtn>
기존에는 url이 변경되는 모든 경우에 routeChangeStart 이벤트가 발생했는데, 제출 버튼이 클릭될 경우 이벤트를 off하는 코드를 추가해주었다.
이제 포스트를 제출할 때 불필요한 페이지 나가기 안내창이 뜨지 않는다.