Keywords: React Router | Navigation Blocking | Prompt Component
Abstract: This technical paper provides an in-depth analysis of multiple approaches for detecting user navigation away from pages in React applications, with a focus on the Prompt component in react-router v4. Through comprehensive code examples and browser compatibility analysis, it offers complete solutions for navigation blocking, covering both route transitions and page refresh/close scenarios.
Introduction
In modern single-page application development, user navigation management represents a critical functional requirement. When users accidentally leave pages during form filling or data editing processes, appropriate prompts are necessary to prevent data loss. React Router, as the most popular routing solution in the React ecosystem, provides multiple mechanisms to handle such scenarios.
Prompt Component in React Router v4
React Router v4 introduced the Prompt component, which serves as the preferred solution for navigation interception. This component integrates declaratively into React components and effectively intercepts route transitions performed through React Router.
Basic implementation example:
import { Prompt } from 'react-router-dom'
const EditPage = ({ hasUnsavedChanges }) => (
<div>
<Prompt
when={hasUnsavedChanges}
message="You have unsaved changes, are you sure you want to leave?"
/>
<!-- Page content -->
</div>
)The Prompt component accepts two main props: when controls when to enable interception, and message defines the prompt message. When users attempt navigation, the browser displays a default confirmation dialog.
Handling Page Refresh and Close
It's important to note that the Prompt component only intercepts route transitions through React Router and cannot handle browser refresh or tab close operations. To comprehensively cover these scenarios, integration with the window.onbeforeunload event is necessary.
Complete implementation solution:
class EditComponent extends React.Component {
componentDidMount() {
this.updateBeforeUnload()
}
componentDidUpdate() {
this.updateBeforeUnload()
}
updateBeforeUnload = () => {
if (this.props.hasUnsavedChanges) {
window.onbeforeunload = () => true
} else {
window.onbeforeunload = null
}
}
render() {
return (
<div>
<Prompt
when={this.props.hasUnsavedChanges}
message="Changes are saved, but not published yet. Do that now?"
/>
<!-- Component content -->
</div>
)
}
}Browser Compatibility Considerations
The onbeforeunload event has varying support across different browsers. Modern browsers generally support this functionality, but the prompt text displayed to users may be subject to browser restrictions and cannot be fully customized. Developers need to test behavior across different browsers in actual projects.
Historical Version Solutions
Prior to React Router v4, developers needed to use different methods to implement navigation interception:
React Router v3 provided the setRouteLeaveHook method:
const ComponentWithRouter = withRouter(
class MyComponent extends React.Component {
componentDidMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
routerWillLeave = (nextLocation) => {
if (this.state.hasUnsavedChanges) {
return 'Your work is not saved! Are you sure you want to leave?'
}
}
render() {
return <div>Component content</div>
}
}
)While this approach provides complete functionality, its behavior during manual URL changes may not be ideal.
Custom History Object Solution
For scenarios requiring finer control, custom history objects can be utilized:
import { createBrowserHistory } from 'history'
const history = createBrowserHistory()
class AdvancedComponent extends React.Component {
componentDidMount() {
this.unblock = history.block((location, action) => {
if (this.state.requiresConfirmation) {
return 'Are you sure you want to navigate to another page?'
}
return true
})
}
componentWillUnmount() {
this.unblock()
}
render() {
return <div>Advanced component content</div>
}
}Best Practice Recommendations
In actual project development, prioritizing the React Router v4 Prompt component is recommended, as it provides the most concise and React-style solution. Simultaneously, decisions regarding supplementary onbeforeunload handling should be based on project requirements. For complex navigation logic, consider using custom history objects to achieve greater flexibility.
Key considerations include timely cleanup of event listeners, accounting for special behaviors in mobile browsers, and providing clear user feedback. Through proper implementation, significant improvements in application user experience and data security can be achieved.