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

Support for blocking navigation (useBeforeLeave) #186

Merged
merged 11 commits into from
Nov 21, 2022

Conversation

Brendan-csel
Copy link
Contributor

@Brendan-csel Brendan-csel commented Oct 18, 2022

Adds this...

useBeforeLeave

useBeforeLeave takes a function that will be called prior to leaving a route. The function will be called with:

  • from (Location): current location (before change).
  • to (string | number}: path passed to navigate.
  • options (NavigateOptions}: options passed to navigate.
  • preventDefault (void function): call to block the route change.
  • defaultPrevented (readonly boolean): true if any previously called leave handlers called preventDefault().
  • retry (void function, force?: boolean ): call to retry the same navigation, perhaps after confirming with the user. Pass true to skip running the leave handlers again (ie force navigate without confirming).

Example usage:

useBeforeLeave((e: BeforeLeaveEventArgs) => {
  if (form.isDirty && !e.defaultPrevented) {
    // preventDefault to block immediately and prompt user async
    e.preventDefault();
    setTimeout(() => {
      if (window.confirm("Discard unsaved changes - are you sure?")) {
        // user wants to proceed anyway so retry with force=true
        e.retry(true); 
      }
    }, 100);
  }
});

NOTE LIMITATIONS

This PR only adds support to block navigations originating from the router (eg <Link>, useNavigate()).

Blocking navigations originating in the browser (back/forward, plain anchors) can also be done and requires either a custom path/hash integration, or solid-router to extend its built-in integrations. This branch includes this addtional behavior.

Finally, users may wish to block full page document navigations (ie using window beforeunload event) but that should be handled in application code directly (outside the scope of the router).

@Brendan-csel
Copy link
Contributor Author

Have added "from" path to event args as it proved useful in real-world testing.

@Brendan-csel
Copy link
Contributor Author

Brendan-csel commented Oct 20, 2022

Hmm, I think there is an issue here requiring a bit of a code restructure. I set up the leave handlers globally - but they should be per <Router>. I'll work on that.

@Brendan-csel
Copy link
Contributor Author

Brendan-csel commented Oct 20, 2022

Okay the latest commit makes the BeforeLeave subscribers specific to the Router instance.

BeforeLeave management can be created by either:

  1. the Router - which means it only supports blocking router initiated navigations.
  2. the integration - which means browser initiated navigations can also be blocked (back/forward, etc).

Note that 2 still requires a change to the integrations which I haven't included in this PR - but do have in this branch (and are using in production O_O!!)

@Brendan-csel Brendan-csel changed the title Support for blocking navigation (onBeforeLeave hook) Support for blocking navigation (useBeforeLeave hook) Oct 21, 2022
@Brendan-csel Brendan-csel changed the title Support for blocking navigation (useBeforeLeave hook) Support for blocking navigation (useBeforeLeave) Oct 21, 2022
@ryansolid
Copy link
Member

This looks really good, and it taps in before Transitions so I think we are good here. Love it.

@ryansolid ryansolid merged commit 6e7bcb4 into solidjs:main Nov 21, 2022
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

Successfully merging this pull request may close these issues.

4 participants