Skip to content

Commit fd8bf85

Browse files
authored
Use precommit handlers instead of after-transition for deferred commits
1 parent 51bbee2 commit fd8bf85

File tree

1 file changed

+20
-14
lines changed

1 file changed

+20
-14
lines changed

README.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ backButtonEl.addEventListener("click", () => {
7474
- [Focus management](#focus-management)
7575
- [Scrolling to fragments and scroll resetting](#scrolling-to-fragments-and-scroll-resetting)
7676
- [Scroll position restoration](#scroll-position-restoration)
77-
- [Deferred commit](#deferred-commit)
77+
- [Precommit handlers](#precommit-handlers)
7878
- [Redirects during deferred commit](#redirects-during-deferred-commit)
7979
- [Transitional time after navigation interception](#transitional-time-after-navigation-interception)
8080
- [Example: handling failed navigations](#example-handling-failed-navigations)
@@ -691,32 +691,38 @@ Some more details on how the navigation API handles scrolling with `"traverse"`
691691
692692
- By default, any navigations which are intercepted with `navigateEvent.intercept()` will _ignore_ the value of `history.scrollRestoration` from the classic history API. This allows developers to use `history.scrollRestoration` for controlling cross-document scroll restoration, while using the more-granular option to `intercept()` to control individual same-document navigations.
693693
694-
#### Deferred commit
694+
#### Precommit handlers
695695
696-
The default behavior of immediately "committing" (i.e., updating `location.href` and `navigation.currentEntry`) works well for most situations, but some developers may find they do not want to immediately update the URL, and may want to retain the option of aborting the navigation without needing to rollback a URL update or cancel-and-restart. This behavior can be customized using `intercept()`'s `commit` option:
696+
The default behavior of immediately "committing" (i.e., updating `location.href` and `navigation.currentEntry`) works well for most situations, but some developers may find they do not want to immediately update the URL, and may want to retain the option of aborting the navigation without needing to rollback a URL update or cancel-and-restart. This behavior can be customized passing a `precommitHandler` callback alongside or instead of the `handler` callback:
697697
698-
- `e.intercept({ handler, commit: "immediate" })`: the default behavior, immediately commit the navigation and update `location.href` and `navigation.currentEntry`.
699-
- `e.intercept({ handler, commit: "after-transition" })`: start the navigation (e.g., show a loading spinner if the UI has one), but do not immediately commit.
698+
- `e.intercept({ handler })`: the default behavior, immediately commit the navigation and update `location.href` and `navigation.currentEntry`.
699+
- `e.intercept({ precommitHandler })`: start the navigation (e.g., show a loading spinner if the UI has one), but do not immediately commit.
700700
701-
When deferred commit is used, the navigation will commit (and a `committed` promise will resolve if present) when `e.commit()` is called. If any handler(s) passed to `intercept()` fulfill, and `e.commit()` has not yet been called, we will fallback to committing just before any `finish` promise resolves and `navigatesuccess` is fired.
701+
The object passed to intercept can include both a `handler` and a `precommitHandler`. If both are included, they are called individually at the appropriate phase.
702702
703-
If a handler passed to `intercept()` rejects before `e.commit()` is called, then the navigation will be treated as canceled (both `committed` and `finished` promises will reject, and no URL update will occur). If a handler passed to `intercept()` rejects after `e.commit()` is called, the behavior will match a rejected promise in immediate commit mode (i.e., the `committed` promise will fulfill, the `finished` promise will reject, and the URL will update).
703+
When precommit handlers are used, the navigation will commit (and a `committed` promise will resolve if present) once all those handlers are fulfilled. Unlike the ordinary `handler`, the `precommitHandler` callbacks are called in sequence—the next `precommit` handler is invoked only when the previous one is fulfilled. That is due to the fact that a precommit handler can asynchronously abort the navigation altogether or redirect the URL, and the next precommit handler should respond to the new state.
704704
705-
Because deferred commit can be used to cancel the navigation before the URL updates, it is only available when `e.cancelable` is true. See [above](#restrictions-on-firing-canceling-and-responding) for details on when `e.cancelable` is set to false, and thus deferred commit is not available.
705+
If a `precommitHandler` passed to `intercept()` rejects, then the navigation will be treated as canceled (both `committed` and `finished` promises will reject, and no URL update will occur).
706+
707+
Because precommit handlers can be used to cancel the navigation before the URL updates, they are only available when `e.cancelable` is true. See [above](#restrictions-on-firing-canceling-and-responding) for details on when `e.cancelable` is set to false, and thus precommit handlers are not available. Calling `intercept()` with a `precommitHandler` on a non-cancelable event would throw a `"SecurityError"` `DOMException`.
706708
707709
#### Redirects during deferred commit
708710
709-
If the `{ commit: "after-transition" }` option is passed to `navigateEvent.intercept()`, then an additional method is available on the `NavigateEvent`: `redirect(url)`. This updates the eventual destination of the `"push"` or `"replace"` navigation. An example usage is as follows:
711+
The `precommitHandler` callback accepts an argument, which is a `controller` that can perform certain actions on the precommitted navigation, in particular redirecting. This updates the eventual destination of the `"push"` or `"replace"` navigation, and restarts the sequence of calling the precommit handlers. An example usage is as follows
710712
711713
```js
712714
navigation.addEventListener("navigate", e => {
713-
e.intercept({ async handler() {
714-
if (await isLoginGuarded(e.destination)) {
715-
e.redirect("/login");
715+
e.intercept({
716+
async precommitHandler(controller) {
717+
if (await isLoginGuarded(e.destination)) {
718+
controller.redirect("/login");
719+
}
716720
}
717721
718-
// Render e.destination, keeping in mind e.destination might be updated.
719-
} });
722+
async handler() {
723+
// apply committed navigation state to document
724+
}
725+
});
720726
});
721727
```
722728

0 commit comments

Comments
 (0)