kit icon indicating copy to clipboard operation
kit copied to clipboard

The `beforeNavigate`/`afterNavigate` `type` is problematic

Open aradalvand opened this issue 3 years ago • 3 comments
trafficstars

Describe the bug

If you click on a link to an external URL on the page, or if you do a goto to an external URL, two successive beforeNavigate events will fire, the first one will have a type of link/goto, and the second one unload:

image

This behavior is strange, to say the least. First of all, beforeNavigate firing twice for the same navigation action is... well, awkward and unexpected. And if you have a navigation loader that you want to show only during client-side routing, you can't simply do this:

beforeNavigation(({ type } => {
    if (type == 'unload') return;
    // Show loader
});

Because then the loader would also appear for external, native browser navigations. The behavior is inconsistent with the "promise" @Rich-Harris made here:

Yep, type === 'unload' and !client would be equivalent. All other types would be client-side navigations

(emphasis mine)

This is now not the case, an instance of beforeNavigate firing with a type of something other than unload (like link in the example I provided above) COULD be a case of non-client-side navigation.

Instead, the API should've probably been designed such that we had unload as a separate boolean flag, alongside the type property, and then the event would only fire once when unloading would happen:

beforeNavigate({ unload, type } => {
    if (unload) return; // e.g. If the user clicked on an external link, here `unload` is `true` and `type` is `link`.
    // Show loader
});

Please feel free to let me know if I'm missing something. Thank you.

Reproduction

Explained above

Logs

No response

System Info

System:
    OS: Linux 5.15 Ubuntu 20.04.4 LTS (Focal Fossa)
    CPU: (4) x64 Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
    Memory: 461.70 MB / 3.77 GB
    Container: Yes
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 16.15.1 - /usr/bin/node
    Yarn: 1.22.15 - /usr/bin/yarn
    npm: 8.11.0 - /usr/bin/npm
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.64 
    @sveltejs/adapter-node: ^1.0.0-next.86 => 1.0.0-next.86 
    @sveltejs/kit: next => 1.0.0-next.480 
    svelte: ^3.44.0 => 3.49.0 
    vite: ^3.1.0-beta.1 => 3.1.0

Severity

serious, but i can work around it

Additional Information

No response

aradalvand avatar Sep 10 '22 23:09 aradalvand

Could you please check this out so that hopefully this is fixed before 1.0? Thanks.

aradalvand avatar Sep 13 '22 16:09 aradalvand

Gave this a stab today but it's not that easy conceptually

  • what if an internal link is clicked but it has the data-sveltekit-reload attribute?
  • what if an internal link is clicked that's a 404 (or some other reason for a full page reload), causing a full page reload?

In both these cases the navigation has to do its first steps in order to determine that it shouldn't do a client side navigation. Question is if we defer calling beforeNavigate and updating $navigating until after that, or if we treat these as internal navigations and don't call beforeNavigate in beforeunload again (the latter is probably my preference).

dummdidumm avatar Sep 14 '22 17:09 dummdidumm

@dummdidumm

what if an internal link is clicked but it has the data-sveltekit-reload attribute? what if an internal link is clicked that's a 404 (or some other reason for a full page reload), causing a full page reload?

Well, in both of those cases a beforeNavigate with unload = true and type = link and origin = [site-url] will be fired. What would be wrong with that?!

In both these cases the navigation has to do its first steps in order to determine that it shouldn't do a client side navigation. Question is if we defer calling beforeNavigate and updating $navigating until after that, or if we treat these as internal navigations and don't call beforeNavigate in beforeunload again (the latter is probably my preference).

What I personally prefer is actually for beforeNavigate and afterNavigate to just be specific to client-side navigation, and if anyone wants to also listen for load and unload they could very easily do so by registering event handlers for the said events on window. I never really liked the idea of beforeNavigate and afterNavigate also firing for native browser navigations — and I have voiced this in the past — when there's already built-in events for that. Currently, in most cases you just have to do an annoying if (type == 'unload') return; or whatever in your handlers all the time.

But if that's a non-starter for you, then at the very least this all should be refined before 1.0. I think the approach I proposed (that is, having unload/load as separate boolean flags alongside type) is more sensible than the current one.

Let me know your thoughts.

aradalvand avatar Sep 15 '22 11:09 aradalvand