Implementing User Leave Detection in React Router

Nov 23, 2025 · Programming · 9 views · 7.8

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.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.