NemoZhong / blog-nemo

0 stars 0 forks source link

Next router does not provide route guards to intercept jumps #5

Open NemoZhong opened 1 year ago

NemoZhong commented 1 year ago

Next JS: Warn User for Unsaved Form before Route Change

πŸ˜‚πŸ˜‚πŸ˜‚ the only way to cancle route change is THROW ERROR!

import Router from 'next/router'
import { useEffect } from 'react'
import { Modal } from 'antd'

const { confirm } = Modal

const usePrompt = (flag: boolean, message: string) => {
  useEffect(() => {
    const beforeRouteHandler = (url: string) => {
      if (Router.pathname !== url) {
        confirm({
          title: message,
          onOk: () => {
            Router.events.off('routeChangeStart', beforeRouteHandler)
            Router.push(url)
          },
          onCancel: () => {},
        })

        Router.events.emit('routeChangeError')
        throw `Route change to "${url}" was aborted (this error can be safely ignored). See https://github.com/zeit/next.js/issues/2476.`
      }
    }
    if (flag) {
      Router.events.on('routeChangeStart', beforeRouteHandler)
    } else {
      Router.events.off('routeChangeStart', beforeRouteHandler)
    }

    return () => Router.events.off('routeChangeStart', beforeRouteHandler)
  }, [flag, message])
}

export default usePrompt
NemoZhong commented 1 year ago

πŸ˜΅β€πŸ’«πŸ˜΅β€πŸ’« both client side rendering and server side rendering in one page, routeChangeStart will not be triggered at service rendering. Like the top answer, beforeunload need to be listenned,but its styles are different

import Router from 'next/router'
import { useEffect } from 'react'
import { Modal } from 'antd'

const { confirm } = Modal

const usePrompt = (flag: boolean, message: string) => {
  useEffect(() => {
    const beforeunloadHandler = (event) => {
      event.preventDefault()
      event.returnValue = message
    }
    const beforeRouteHandler = (url: string) => {
      if (Router.pathname !== url) {
        confirm({
          title: message,
          onOk: () => {
            Router.events.off('routeChangeStart', beforeRouteHandler)
            Router.push(url)
          },
          onCancel: () => {},
        })

        Router.events.emit('routeChangeError')
        throw `Route change to "${url}" was aborted (this error can be safely ignored). See https://github.com/zeit/next.js/issues/2476.`
      }
    }

    if (flag) {
      Router.events.on('routeChangeStart', beforeRouteHandler)
      window.addEventListener('beforeunload', beforeunloadHandler)
    } else {
      Router.events.off('routeChangeStart', beforeRouteHandler)
      window.removeEventListener('beforeunload', beforeunloadHandler)
    }

    return () => {
      Router.events.off('routeChangeStart', beforeRouteHandler)
      window.removeEventListener('beforeunload', beforeunloadHandler)
    }
  }, [flag, message])
}

export default usePrompt