Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some kind of onbeforeunload for routes? #22

Open
mattmccray opened this issue Jul 9, 2021 · 2 comments
Open

Some kind of onbeforeunload for routes? #22

mattmccray opened this issue Jul 9, 2021 · 2 comments

Comments

@mattmccray
Copy link

Perhaps I'm missing something, but is there a way to register a callback for route changes... And cancel them? Like an onbeforeunload but for routes?

function TestScreen(props) {
  const route = useRoute()

  route.beforeTransition(e => { // maybe 'e' contains target route info?
    // (user code to check for form changes...)
   return false // false to prevent transition?
  })

  return (
    <form> . . . </form>
  )
}

I don't know about the api specifically, that's just as an example of potential usage.

@rturnq
Copy link
Owner

rturnq commented Jul 10, 2021

Yeah, there currently isn't a way to do this. I agree it would be a worthwhile feature to add. Thinking about it brings up a lot of questions how exactly it should work and what the expectations are. Just thinking out loud here:

The handlers would have to operate at the router/location level but you would likely want to register them on a route close to your form or whatever (like you illustrated). So if you had multiple registered on say a parent route and a child route, and the location change affected both routes, would they both fire? Or maybe just the root-most one? If they did both fire then if either rejected the transition would be cancelled.

Cancellation of navigation initiated from the router would be easy enough to handle but locations changes from the integrated system like history.popState will have to be more like a revert so I'll have to figure out how to make that work correctly.

I'm going to think about this a bit and see how others like React-Router handle this.

@mattmccray
Copy link
Author

It's definitely tricky.

Just my two cents:

  • Route change handlers could be registered at the route level -- Either via the <MatchRoute> component or from the useRoute() hook.
  • Any handler, at any level, that returned falsy would cancel the navigation -- Instead of the old return false from an event handler approach, it could use an event.returnValue = "" or even event.preventDefault()... Maybe the latter is best?
  • Which is all fine and dandy with internal navigation, as you said, but trickier when the navigation is external -- back button or manually entered URL.
  • And I foresee added difficulty with hash navigation. With the History API, you can use replaceState to revert to its last value without adding to the browser's navigation history. But I don't think such a thing is possible with hash navigation.

Interestingly, React Router doesn't seem to support routing events, per se. They expose a Prompt component that when when={true} will show a browser prompt rather like the onbeforeunload one...

<Prompt when={isBlocking}
  message={location =>`Are you sure you want to go to ${location.pathname}`}
/>

Haven't dug into the internals there, and I'm not sure how I feel about that... Actually, I am sure. "Prompt" is a horribly named component. 😄 But using a component to control the state of whether a navigation should be allowed is intriguing.

Perhaps this is cleaner, come to think of it. You don't really have hooks to intercept route changes, but you do have a way to prevent navigation without notifying the user.

Granted, component or no component, the internals would need to support all the prevention and rolling back of the navigation state.

Alright, now I'm just rambling. Sorry. For the app I'm working on, I'm moving to an "all CRUD objects are mutated in modal dialogs" style of UI. It doesn't really address the back button issue, but I can easily disable/disallow internal navigation this way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants