kit icon indicating copy to clipboard operation
kit copied to clipboard

`$page.url.hash` is not updated.

Open hyunbinseo opened this issue 2 years ago • 4 comments

Describe the bug

When the URL hash value is changed using the address bar, the $page store is not updated.

Reproduction

Checked on Chrome, Firefox, and Safari. Reference 'System Info' for version numbers.

<script>
  import { page } from '$app/stores';
  
  $: console.log($page.url.hash);
</script>

<svelte:window
  on:hashchange={() => {
    console.log($page.url.hash, 'store');
    console.log(window.location.hash, 'location')
  }}
/>

<a href="#1">#1</a>
  1. Load the page
  2. Click the anchor element.
  3. Replace #1 with #2 in the address bar.
  4. Check the console.
<!-- Anchor is clicked. Hash is now #1 -->
#1
#1 store
#1 location

<!-- URL is updated. Hash is now #2 -->
#1 store
#2 location

Logs

No response

System Info

System:
  OS: macOS 13.2.1
  CPU: (8) arm64 Apple M1
  Memory: 78.86 MB / 8.00 GB
  Shell: 5.8.1 - /bin/zsh
Binaries:
  Node: 18.14.1 - ~/.nvm/versions/node/v18.14.1/bin/node
  npm: 9.3.1 - ~/.nvm/versions/node/v18.14.1/bin/npm
Browsers:
  Chrome: 110.0.5481.177
  Firefox: 110.0
  Safari: 16.3
npmPackages:
  @sveltejs/adapter-auto: ^2.0.0 => 2.0.0 
  @sveltejs/kit: ^1.5.0 => 1.11.0 
  svelte: ^3.54.0 => 3.55.1 
  vite: ^4.0.0 => 4.1.4

Severity

annoyance

Additional Information

No response

hyunbinseo avatar Mar 09 '23 04:03 hyunbinseo

@Rich-Harris Just to brainstorm: one of the ways to deal with this is to update instances of the stores of page and url whenever the event hashchanges is emitted?

https://github.com/sveltejs/kit/blob/f1beb92474dafa628fda2d1b3ae26d8e9d49ff2a/packages/kit/src/runtime/client/client.js#L1693-L1704

This is where the magic should happen? 🤔

Accessing the stores via:

https://github.com/sveltejs/kit/blob/f1beb92474dafa628fda2d1b3ae26d8e9d49ff2a/packages/kit/src/runtime/client/singletons.js#L16-L21

I've seen the other pr's related to the hashchange event like #3177 but can't see through the problem yet 👀

vicentematus avatar Mar 23 '23 05:03 vicentematus

Here's what I've been trying: the hashchange event emits an special event called HashChangeEvent which contails newURL and oldURL properties. Whenever we detect a change in the hash, we compare if there has been a change (maybe this is redundant).

But whenever i try to update the store.page inside the svelte:hashchange acts kinda weird. Like there is some kinda race or something.

Lets say we update the page with the new hash on the hashchange event:

addEventListener('hashchange', (event) => {
    const new_url = new URL(event.newURL);
    const old_url = new URL(event.oldURL);
    // if the hashchange happened as a result of clicking on a link,
    // code here...

    // If the hash is the only thing that changed via user input, update the hash on the store
    if (new_url.hash != old_url.hash) {
	stores.page.set({ ...page, url: { ...page.url, hash: new_url.hash } });
	stores.page.notify();
	return;
    }

And this is the code for +page.svelte

<script>
	import { page } from '$app/stores';

	$: console.log('Store', $page.url.hash);
</script>

<svelte:window
	on:hashchange={() => {
	        console.log($page.url.hash, 'store');
		console.log(window.location.hash, 'location');
		
	}}
/>

If we replace #1 with #2 in the address bar, the expected output is 2. This is the scenario:

  1. ❗ The console.log of $page.url.hash will print: 1
  2. console.log of window.location.hash will print: 2
  3. ✅ the reactive $page.url.hash outside of the hashchange event will print the correct output: 2.

It's like hashchange event is not aware of the store changes? just after the first update of the store it will recognize it.

vicentematus avatar Mar 28 '23 04:03 vicentematus

Hello!

Would a change such as this be acceptable?

// This is within an else block for if (hash_navigating)

// In the occasion of the hash being updated directly through
// the browser's address bar, the page store needs to be
// updated
const url = new URL(location.href, document.baseURI);

current.url = url;

stores.page.set({ ...page, url });
stores.page.notify();

It seems to work with shallow, manual tests, but I am not sure about other implications. Furthermore, I've been unable to write a Playwright test to change the address bar and check the value's change in a div whose content is $page.url.

GabrielBarbosaGV avatar Mar 28 '23 18:03 GabrielBarbosaGV

exclamation The console.log of $page.url.hash will print: 1

I think this may be the same bug I have run into in #10013

UPDATE: Doesn't appear so

moonmeister avatar May 23 '23 02:05 moonmeister