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

page props are re-set whenever the session store is updated, and the load function is re-run the first time #3732

Closed
ponderingexistence opened this issue Feb 4, 2022 · 10 comments · Fixed by #5654
Labels
bug Something isn't working load / layout p1-important SvelteKit cannot be used by a large number of people, basic functionality is missing, etc.
Milestone

Comments

@ponderingexistence
Copy link

ponderingexistence commented Feb 4, 2022

Describe the bug

In the docs, in the section about the load function, it's stated that:

...if url, session or stuff are used in the function, they will be re-run whenever their value changes... (here)

But this doesn't seem to be the case. I have the following page in my SvelteKit app:
index.svelte:

<script lang="ts" context="module">
    import type { Load } from '@sveltejs/kit';

    export const load: Load = () => {
        console.warn('index load function');
        return {};
    }
</script>

<script lang="ts">
    import { session } from '$app/stores';

    function changeSession() {
        $session.someField = Math.random();
    }
</script>

<pre>{JSON.stringify($session, null, 4)}</pre>

<button on:click={changeSession}>
    Change $session
</button>

If you click on the "Change $session" button, you'll see the message "index load function" appearing in the console, which obviously means the load function is re-run, even though session isn't used in the load function. This is inconsistent with the behavior described in the docs.

The strange thing, however, is that from the second time onwards, when the value of the '$session' store changes, the load function is no longer re-run. This is confusing.

The more severe bug, however, which seems to be related to this one, is that every time the $session store changes, the page's props are apparently re-set, which means reactivity is re-triggered throughout the entire page, potentially causing massive problems.

Consider the following:

<script lang="ts" context="module">
    import type { Load } from '@sveltejs/kit';

    export const load: Load = () => {
        console.warn('index load function');
        return {
            props: {
                userBasicInfo: {
                    id: 2,
                }
            }
        };
    }
</script>

<script lang="ts">
    import { browser } from '$app/env';
    import { session } from '$app/stores';

    export let userBasicInfo;

    function changeSession() {
        $session.someField = Math.random();
    }

    $: console.log('userBasicInfo changed:', userBasicInfo);
</script>

<pre>{JSON.stringify($session, null, 4)}</pre>

<button on:click={changeSession}>
    Change Ssession
</button>

<hr />

{#if browser}
    {#await fetch(`https://reqres.in/api/users/${userBasicInfo.id}`).then(r => r.json())}
        Loading...
    {:then result}
        <pre>{JSON.stringify(result, null, 4)}</pre>
    {/await}
{/if}

In this setup, whenever you click on the "Change $session" button — which is to say whenever you update the $session store — the fetch request in the markup, which uses the userBaicInfo prop, is re-triggered, even though it obviously shouldn't. And you can see the userBasicInfo changed: ... message in the console which proves that it's because of the userBasicInfo prop getting re-set.

Reproduction

You can create an empty SvelteKit skeleton project and then copy-paste the two code snippets above into the index.svelte file, and then npm run dev => open your browser and see the behavior for yourself.

Logs

No response

System Info

System:
    OS: Windows 10 10.0.19042
    CPU: (4) x64 Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
    Memory: 1.09 GB / 7.89 GB
  Binaries:
    Node: 14.15.0 - C:\Program Files\nodejs\node.EXE     
    Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD       
    npm: 6.14.8 - C:\Program Files\nodejs\npm.CMD        
  Browsers:
    Edge: Spartan (44.19041.1266.0), Chromium (97.0.1072.76)
    Internet Explorer: 11.0.19041.1202
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.17
    @sveltejs/kit: next => 1.0.0-next.260
    svelte: ^3.44.0 => 3.46.4

Severity

blocking all usage of SvelteKit

Additional Information

No response

@aolose
Copy link
Contributor

aolose commented Feb 5, 2022

Another bug ... may be.
if remove the load function then change the session , page won't make another request. It looks good.
but after a page modified, change session then page do the request again.

example:

<script context="module">
</script>

<script>
    import {browser} from '$app/env';
    import {session} from '$app/stores';

    export let userBasicInfo={id:1};

    function changeSession() {
        $session.someField = Math.random();
    }
    $: console.log('userBasicInfo changed:', userBasicInfo);
</script>

<pre>{JSON.stringify($session, null, 4)}</pre>

<button on:click={changeSession}>
    Change Ssession
</button>

<hr/>

{#if browser}
    {#await fetch(`https://reqres.in/api/users/${userBasicInfo.id}`).then(r => r.json())}
        Loading...
    {:then result}
        <pre>{JSON.stringify(result, null, 4)}</pre>
    {/await}
{/if}

It will happen when a ssr page is modified
then the manifest file at http://localhost:3000/.svelte-kit/generated/manifest.js will change.

for example:
first time run dev:
manifest.js will be

const c = [
	() => import('/src/routes/__layout.svelte'),
	() => import('/.svelte-kit/runtime/components/error.svelte'),
	() => import('/src/routes/index.svelte')
];

after you modify index.svelte:
before you reload page:
manifest.js will be

const c = [
	() => import("..\\..\\src\\routes\\__layout.svelte"),
	() => import("..\\runtime\\components\\error.svelte"),
	() => import("..\\..\\src\\routes\\index.svelte")
];

after you reload page:
manifest.js will be:

const c = [
	() => import('/src/routes/__layout.svelte'),
	() => import('/.svelte-kit/runtime/components/error.svelte'),
	() => import('/src/routes/index.svelte?t=1644064437131')
];

as you see there is a version number append to the module.

When session changed page will renderer and modules will load :

// preload modules
a.forEach((loader) => loader());

but modules in page render from server have different path

start({
	target: document.querySelector('[data-hydrate="lf6ho7"]').parentNode,
	paths: {"base":"","assets":""},
	session: {},
	route: true,
	spa: false,
	trailing_slash: "never",
	hydrate: {
		status: 200,
		error: null,
		nodes: [
			import("/src/routes/__layout.svelte"),
				import("/src/routes/index.svelte")
		],
		url: new URL("http://localhost:3000/"),
		params: {}
	}
});

so...same page but from different modules will execute.

@ponderingexistence
Copy link
Author

ponderingexistence commented Feb 5, 2022

@aolose I'm not sure I understand what you're saying, but I don't think it answers the question of why props are reset when session changes. All I know is that that shouldn't happen.

It will happen when a ssr page is modified

What do you mean?! I'm not modifying the page.

@ponderingexistence
Copy link
Author

ponderingexistence commented Feb 5, 2022

I would appreciate a response from the maintainers, this behavior doesn't make sense to me and is creating countless problems and causing me a lot of headache... :(

@aolose
Copy link
Contributor

aolose commented Feb 5, 2022

@aolose I'm not sure I understand what you're saying, but I don't think it answers the question of why props are reset when session changes. All I know is that that shouldn't happen.

It will happen when a ssr page is modified

What do you mean?! I'm not modifying the page.

emmm... I tried to reproduce your problem then i found my reply was wrong~

@ponderingexistence
Copy link
Author

Any ideas, anyone?

@rmunn
Copy link
Contributor

rmunn commented Feb 21, 2022

One thing I've found so far is that the load() function running twice has to do with SSR. If I put that into a page.svelte route, and load the index.svelte route first and then follow a link from index.svelte to page.svelte, the load() function of page.svelte only runs once. I'll need a bit more time to track that down, but it's one data point.

As for $session triggering props being re-set, I can reproduce that as well, but in the limited time I have available today I wasn't able to find the cause. I'll continue to work on it next time I have some spare time, but that might not be for a couple of days. Just wanted to let you know that your issue isn't being ignored.

@ponderingexistence
Copy link
Author

ponderingexistence commented Feb 21, 2022

Hi @rmunn, thank you for the response.
Let me also point out real quick that it's actually the $session causing props to be reset that's the more pressing issue, the only reason I also mentioned the load function being run twice was because I thought it may have something to do with the $session change resetting props, but I'm not sure.

Needless to say, props being reset whenever $session changes can cause many unexpected problems for users, I'm not sure why it happens but I think it should be considered a bug.

I'll continue to work on it next time I have some spare time, but that might not be for a couple of days. Just wanted to let you know that your issue isn't being ignored.

Good to hear, thank you :) <3

@Rich-Harris Rich-Harris added the bug Something isn't working label Mar 4, 2022
@Rich-Harris Rich-Harris added this to the 1.0 milestone Mar 5, 2022
@benmccann benmccann added the p1-important SvelteKit cannot be used by a large number of people, basic functionality is missing, etc. label Mar 17, 2022
@DrDanRyan
Copy link

Needless to say, props being reset whenever $session changes can cause many unexpected problems for users, I'm not sure why it happens but I think it should be considered a bug.

Agreed, this has been driving me nuts for days until I finally became sure that it was a bug with Sveltekit and not me just being stupid. To add to the strangeness, you can even change the page props and then call a setTimeout for a large amount of time to set the $session store value and after the $session store value gets set the page props will revert to their previous value. How is this even possible!?

@aradalvand
Copy link
Contributor

aradalvand commented Jun 30, 2022

@DrDanRyan Yeah unfortunately this bug is still present in SvelteKit and can cause a lot of cofusion and headache, it's planned for v1.0 so I very much hope it will be fixed soon.
image

@DrDanRyan
Copy link

If its any help, this is my minimal reproduction of the bug you can see that the props (user in this case) get reset when the $session assignment is made 2 seconds after the post call is resolved.

// index.svelte
<script>
	import { session } from '$app/stores';
	export let user;

	async function submit() {
		const response = await fetch('/', {
			method: 'POST',
			headers: { accept: 'application/json' }
		});
		const body = await response.json();
		user = body.user;
		setTimeout(() => {
			$session.message = body.message;
		}, 2000);
	}
</script>

<div>{JSON.stringify($session)}</div>
<p>Hello {user}!</p>
<button on:click={submit}>Submit</button>
// index.js
export async function get() {
	return {
		body: {
			user: 'Bob'
		}
	};
}

export async function post() {
	return {
		body: {
			user: 'Tom',
			message: 'I come from the post call!'
		}
	};
}

Rich-Harris pushed a commit that referenced this issue Jul 25, 2022
* [fix] Prevent rerender when route state did not change

Fixes #3732

* adjust code to fix only session case, tests

* format

* fix test expectation

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working load / layout p1-important SvelteKit cannot be used by a large number of people, basic functionality is missing, etc.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants