在Next JS中检测用户何时离开页面

kq0g1dla  于 2023-04-11  发布在  其他
关注(0)|答案(9)|浏览(288)

我想检测用户何时离开页面Next JS.我数了3种离开页面的方式:
1.通过点击链接
1.通过执行触发router.back、router.push等的操作...
1.通过关闭选项卡(即当触发beforeunload事件时
能够检测页面何时离开是非常有帮助的,例如,提醒用户某些更改尚未保存。
我想要的东西像:

router.beforeLeavingPage(() => {
    // my callback
})
tvmytwxo

tvmytwxo1#

我使用“next/router”,如NextJs Page来断开套接字

import { useEffect } from 'react'
import { useRouter } from 'next/router'

export default function MyPage() {
    const router = useRouter()

    useEffect(() => {
        const exitingFunction = () => {
            console.log('exiting...');
        };

        router.events.on('routeChangeStart', exitingFunction );

        return () => {
            console.log('unmounting component...');
            router.events.off('routeChangeStart', exitingFunction);
        };
    }, []);

    return <>My Page</>
}
gudnpqoy

gudnpqoy2#

router.beforePopState是伟大的浏览器后退按钮,但不是为<Link>的网页上。
解决方案在这里找到:https://github.com/vercel/next.js/issues/2694#issuecomment-732990201
...这里是一个版本的这种方法,对于任何人谁得到这个页面寻找另一种解决方案.注意,我已经适应了我的要求有点进一步.

// prompt the user if they try and leave with unsaved changes  
useEffect(() => {
  const warningText =
    'You have unsaved changes - are you sure you wish to leave this page?';
  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');
    throw 'routeChange aborted.';
  };
  window.addEventListener('beforeunload', handleWindowClose);
  router.events.on('routeChangeStart', handleBrowseAway);
  return () => {
    window.removeEventListener('beforeunload', handleWindowClose);
    router.events.off('routeChangeStart', handleBrowseAway);
  };
}, [unsavedChanges]);

到目前为止,它似乎工作得相当可靠。
或者,您可以自己将onClick添加到所有<Link>中。

r1zk6ea1

r1zk6ea13#

您可以在此处使用router.beforePopState检查示例

rt4zxlrg

rt4zxlrg4#

我在写代码的时候看到了两件事:

  • 知道nextjs路由器何时被激活
  • 了解特定浏览器事件何时发生

我用这种方法做了一个钩子,如果使用下一个路由器,或者有一个经典的浏览器事件(关闭标签页,刷新),它就会触发

import SingletonRouter, { Router } from 'next/router';

export function usePreventUserFromErasingContent(shouldPreventLeaving) {
  const stringToDisplay = 'Do you want to save before leaving the page ?';

  useEffect(() => {
    // Prevents tab quit / tab refresh
    if (shouldPreventLeaving) {
      // Adding window alert if the shop quits without saving
      window.onbeforeunload = function () {
        return stringToDisplay;
      };
    } else {
      window.onbeforeunload = () => {};
    }

    if (shouldPreventLeaving) {
      // Prevents next routing
      SingletonRouter.router.change = (...args) => {
        if (confirm(stringToDisplay)) {
          return Router.prototype.change.apply(SingletonRouter.router, args);
        } else {
          return new Promise((resolve, reject) => resolve(false));
        }
      };
    }
    return () => {
      delete SingletonRouter.router.change;
    };
  }, [shouldPreventLeaving]);
}

你只需要在你想要覆盖的组件中调用你的钩子:

usePreventUserFromErasingContent(isThereModificationNotSaved);

这是我用useState创建的一个布尔值,在需要时进行编辑。这样,它只在需要时触发。

kiayqfof

kiayqfof5#

您可以在react页面或组件中使用默认的web api's eventhandler

if (process.browser) {
  window.onbeforeunload = () => {
    // your callback
  }
}
ktca8awb

ktca8awb6#

浏览器严重限制权限和功能,但这是有效的:

  • window.confirm:用于next.js路由器事件
  • beforeunload:用于浏览器重新加载,关闭选项卡或导航离开
import { useRouter } from 'next/router'

const MyComponent = () => {
  const router = useRouter()
  const unsavedChanges = true
  const warningText =
    'You have unsaved changes - are you sure you wish to leave this page?'

  useEffect(() => {
    const handleWindowClose = (e) => {
      if (!unsavedChanges) return
      e.preventDefault()
      return (e.returnValue = warningText)
    }
    const handleBrowseAway = () => {
      if (!unsavedChanges) return
      if (window.confirm(warningText)) return
      router.events.emit('routeChangeError')
      throw 'routeChange aborted.'
    }
    window.addEventListener('beforeunload', handleWindowClose)
    router.events.on('routeChangeStart', handleBrowseAway)
    return () => {
      window.removeEventListener('beforeunload', handleWindowClose)
      router.events.off('routeChangeStart', handleBrowseAway)
    }
  }, [unsavedChanges])

}
export default MyComponent

此信用article

nuypyhwy

nuypyhwy7#

这在next-router / react-FC中对我有效
1.添加路由器事件处理程序
1.添加onBeforeUnload事件处理程序
1.卸载组件时卸载它们
https://github.com/vercel/next.js/issues/2476#issuecomment-563190607

hiz5n14c

hiz5n14c8#

我写了一篇中等的文章Prevent Route Changes and Unsaved Data Loss in Next.js。下面是完整的代码:

import SingletonRouter, { Router } from 'next/router';
import { useEffect } from 'react';

const defaultConfirmationDialog = async (msg?: string) => window.confirm(msg);

/**
 * React Hook
 */
export const useLeavePageConfirmation = (
  shouldPreventLeaving: boolean,
  message: string = 'Changes you made may not be saved.',
  confirmationDialog: (msg?: string) => Promise<boolean> = defaultConfirmationDialog
) => {
  useEffect(() => {
    // @ts-ignore because "change" is private in Next.js
    if (!SingletonRouter.router?.change) {
      return;
    }

    // @ts-ignore because "change" is private in Next.js
    const originalChangeFunction = SingletonRouter.router.change;
    const originalOnBeforeUnloadFunction = window.onbeforeunload;

    /*
     * Modifying the window.onbeforeunload event stops the browser tab/window from
     * being closed or refreshed. Since it is not possible to alter the close or reload
     * alert message, an empty string is passed to trigger the alert and avoid confusion
     * about the option to modify the message.
     */
    if (shouldPreventLeaving) {
      window.onbeforeunload = () => '';
    } else {
      window.onbeforeunload = originalOnBeforeUnloadFunction;
    }

    /*
     * Overriding the router.change function blocks Next.js route navigations
     * and disables the browser's back and forward buttons. This opens up the
     * possibility to use the window.confirm alert instead.
     */
    if (shouldPreventLeaving) {
      // @ts-ignore because "change" is private in Next.js
      SingletonRouter.router.change = async (...args) => {
        const [historyMethod, , as] = args;
        // @ts-ignore because "state" is private in Next.js
        const currentUrl = SingletonRouter.router?.state.asPath.split('?')[0];
        const changedUrl = as.split('?')[0];
        const hasNavigatedAwayFromPage = currentUrl !== changedUrl;
        const wasBackOrForwardBrowserButtonClicked = historyMethod === 'replaceState';
        let confirmed = false;

        if (hasNavigatedAwayFromPage) {
          confirmed = await confirmationDialog(message);
        }

        if (confirmed) {
          // @ts-ignore because "change" is private in Next.js
          Router.prototype.change.apply(SingletonRouter.router, args);
        } else if (wasBackOrForwardBrowserButtonClicked && hasNavigatedAwayFromPage) {
          /*
           * The URL changes even if the user clicks "false" to navigate away from the page.
           * It is necessary to update it to reflect the current URL.
           */
          // @ts-ignore because "state" is private in Next.js
          await SingletonRouter.router?.push(SingletonRouter.router?.state.asPath);

          /*
           * @todo
           *   I attempted to determine if the user clicked the forward or back button on the browser,
           *   but was unable to find a solution after several hours of effort. As a result, I temporarily
           *   hardcoded it to assume the back button was clicked, since that is the most common scenario.
           *   However, this may cause issues with the URL if the forward button is actually clicked.
           *   I hope that a solution can be found in the future.
           */
          const browserDirection = 'back';

          browserDirection === 'back'
            ? history.go(1) // back button
            : history.go(-1); // forward button
        }
      };
    }

    /*
     * When the component is unmounted, the original change function is assigned back.
     */
    return () => {
      // @ts-ignore because "change" is private in Next.js
      SingletonRouter.router.change = originalChangeFunction;
      window.onbeforeunload = originalOnBeforeUnloadFunction;
    };
  }, [shouldPreventLeaving, message, confirmationDialog]);
};
aydmsdu9

aydmsdu99#

您可以使用react-use npm package

import { useEffect } from "react";
import Router from "next/router";
import { useBeforeUnload } from "react-use";

export const useLeavePageConfirm = (
  isConfirm = true,
  message = "Are you sure want to leave this page?"
) => {
  useBeforeUnload(isConfirm, message);

  useEffect(() => {
    const handler = () => {
      if (isConfirm && !window.confirm(message)) {
        throw "Route Canceled";
      }
    };

    Router.events.on("routeChangeStart", handler);

    return () => {
      Router.events.off("routeChangeStart", handler);
    };
  }, [isConfirm, message]);
};

相关问题