使用 React Router v5 检测页面刷新、标签关闭和路由更改问题:解决方案:

2025-06-05

使用 React Router v5 检测页面刷新、标签关闭和路由更改

问题:

解决方案:

想象一下,在填写完一份强制性且无聊的调查问卷后,你不小心关闭了浏览器标签页。你所有的回复现在都丢失了。

真让人沮丧,不是吗?

您肯定不想让您的用户有这样的体验,下面是您可以解决此问题的方法。

问题:

当用户意外...时如何提示用户

  1. 重新加载页面。
  2. 关闭浏览器选项卡或窗口。
  3. 按下浏览器的后退按钮。
  4. 单击链接/更改路线。

解决方案:

第一部分:检测页面重新加载和浏览器标签页关闭

选项卡/窗口关闭或页面重新加载事件意味着当前文档及其资源将被移除(卸载)。在这种情况下,beforeunload会触发该事件。

在事件触发时beforeunload,文档仍然可见,并且事件是可取消的,这意味着unload可以阻止该事件,就好像它从未发生过一样。

此事件使网页能够触发确认对话框,询问用户是否确实要离开该页面。如果用户确认,浏览器将导航到新页面;否则,浏览器将取消导航。

预防beforeunload事件



window.onbeforeunload = (event) => {
  const e = event || window.event;
  // Cancel the event
  e.preventDefault();
  if (e) {
    e.returnValue = ''; // Legacy method for cross browser support
  }
  return ''; // Legacy method for cross browser support
};


Enter fullscreen mode Exit fullscreen mode

以上3种方法均有效e.preventDefault()e.returnValue = ''return ''阻止事件执行。

确认框显示示例:

重新加载站点提示

离开站点提示

注意:遗憾的是,并非所有浏览器都支持自定义消息

根据状态显示提示

#1创建一个以 React 状态showExitPrompt作为参数的函数,并onbeforeunload在函数内部初始化监听器。在事件监听器内部使用该状态。

为什么要将 React 状态作为参数传递?
因为它onbeforeunload是一个原生的 JavaScript 事件监听器,任何 React 状态的改变都不会更新其回调函数中的状态。



import { useState } from 'react';

const initBeforeUnLoad = (showExitPrompt) => {
  window.onbeforeunload = (event) => {
    // Show prompt based on state
    if (showExitPrompt) {
      const e = event || window.event;
      e.preventDefault();
      if (e) {
        e.returnValue = ''
      }
      return '';
    }
  };
};


Enter fullscreen mode Exit fullscreen mode

#2创建状态showExitPrompt来管理提示并在页面加载时注册事件监听器。



function MyComponent() {
  const [showExitPrompt, setShowExitPrompt] = useState(false);

  // Initialize the beforeunload event listener after the resources are loaded
  window.onload = function() {
    initBeforeUnLoad(showExitPrompt);
  };
}


Enter fullscreen mode Exit fullscreen mode

#3在状态改变时重新初始化事件监听器。



import { useState, useEffect } from 'react';

const initBeforeUnLoad = (showExitPrompt) => {
  // … code
}

function MyComponent() {
  const [showExitPrompt, setShowExitPrompt] = useState(false);

  window.onload = function() {
    initBeforeUnLoad(showExitPrompt);
  };

  // Re-Initialize the onbeforeunload event listener
  useEffect(() => {
    initBeforeUnLoad(showExitPrompt);
  }, [showExitPrompt]);
}


Enter fullscreen mode Exit fullscreen mode

现在您可以在组件中使用它了。但是,在应用程序的任何地方创建自定义钩子来设置和访问状态更为高效。

使用自定义钩子

#1钩子文件useExitPrompt.js



import { useState, useEffect } from 'react';

const initBeforeUnLoad = (showExitPrompt) => {
  window.onbeforeunload = (event) => {
    if (showExitPrompt) {
      const e = event || window.event;
      e.preventDefault();
      if (e) {
        e.returnValue = '';
      }
      return '';
    }
  };
};

// Hook
export default function useExitPrompt(bool) {
  const [showExitPrompt, setShowExitPrompt] = useState(bool);

  window.onload = function() {
    initBeforeUnLoad(showExitPrompt);
  };

  useEffect(() => {
    initBeforeUnLoad(showExitPrompt);
  }, [showExitPrompt]);

  return [showExitPrompt, setShowExitPrompt];
}


Enter fullscreen mode Exit fullscreen mode

#2组件文件MyComponent.js
注意:showExitPrompt卸载组件时,必须将状态值重置为默认值。



import useExitPrompt from './useExitPrompt.js'

export default function MyComponent() {
  const [showExitPrompt, setShowExitPrompt] = useExitPrompt(false);

  const handleClick = (e) => {
    e.preventDefault();
    setShowExitPrompt(!showExitPrompt)
  }

  //NOTE: this similar to componentWillUnmount()
  useEffect(() => {
    return () => {
      setShowExitPrompt(false)
    }
  }, [])

  return (
    <div className="App">
      <form>{/*Your code*/}</form>
      <button onClick={handleClick}>Show/Hide the prompt</button>
      <Child setShowExitPrompt={setShowExitPrompt} />
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

或者

#2组件文件App.js
通过将其传递给您的子组件,并使用应用程序中任何地方的钩子Context.Provider访问该值。useContext()



import useExitPrompt from './useExitPrompt.js'
import MyContext from './MyContext.js'

export default function App() {
  const [showExitPrompt, setShowExitPrompt] = useExitPrompt(false);

  return (
    <div className="App">
      <MyContext.Provider value={{showExitPrompt, setShowExitPrompt}}>
        <MyMainApp />
      </MyContext.Provider>
    </div>
  );
}

export default function MyComponent() {
  const { showExitPrompt, setShowExitPrompt } = useContext(MyContext);

  //NOTE: this works similar to componentWillUnmount()
  useEffect(() => {
    return () => {
      setShowExitPrompt(false);
    }
  }, [])

  return (
    <div>{/* your code */}</div>
  );
}


Enter fullscreen mode Exit fullscreen mode

第 2 部分:检测路由/页面更改和浏览器返回

与上述操作类似,当用户点击链接时,他们会被重定向到一个新页面,并且文档及其资源将被卸载。

但是,React Router 的工作方式有所不同,它实现了History API,可以访问浏览器的会话历史记录。点击常规链接,您将进入新的 URL 和新的文档(页面),同时history允许您在不离开页面的情况下“伪造” URL。

location.pathname对比history.pushState()



window.location.pathname = '/dummy-page'


Enter fullscreen mode Exit fullscreen mode

window.location.pathname 演示

垂直/垂直



window.history.pushState({}, '', '/dummy-page')


Enter fullscreen mode Exit fullscreen mode

window.history.pushState 演示

你看出区别了吗?history.pushState()只改变了 URL,没有其他变化,整个页面保持不变,同时location.pathname将您重定向到该新页面,可能会出现 404 错误,因为这样的路由不存在。

getUserConfirmation()使用和<Prompt/>组件显示提示

React Router 提供了一个 propgetUserConfirmation()<BrowserRouter>确认导航,以及一个组件<Prompt/>来显示来自子组件的自定义消息。

#1根文件App.js



import { BrowserRouter } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter getUserConfirmation={(message, callback) => {
      // this is the default behavior
      const allowTransition = window.confirm(message);
      callback(allowTransition);
      }}
    >
      <Routes />
    </BrowserRouter>
  );
}


Enter fullscreen mode Exit fullscreen mode

window.confirm()<Prompt />将从相应的子组件中显示你传入 React Router 组件的消息。该callback()函数需要一个布尔参数来阻止跳转到新页面。

#2组件文件MyForm.js
<Prompt />有 2 个 props,whenmessage。如果whenprop 的值设置为 true,并且用户点击了不同的链接,则会使用messageprops 中传递的消息提示用户。



import { Prompt } from 'react-router-dom';

function MyForm() {
  const [isFormIncomplete, setIsFormIncomplete] = useState(true);
  return (
    <div>
     <form>{/*Your code*/}</form>

     <Prompt
       when={isFormIncomplete}
       message="Are you sure you want to leave?" />
    </div>
  )
}


Enter fullscreen mode Exit fullscreen mode

确认框显示示例:
路线变更提示

概括

如果用户的操作...

  1. 删除页面的资源,使用beforeunload原始 JavaScript 事件来提示用户。
  2. 仅更改视图,与组件一起getUserConfirmation()使用来提示用户。<BrowserRouter/><Prompt />
文章来源:https://dev.to/eons/detect-page-refresh-tab-close-and-route-change-with-react-router-v5-3pd
PREV
首个 React Web 应用程序专用 IDE 终于来了 - ReacTide 3.0 Beta
NEXT
🏆 超棒的克隆