kit
kit copied to clipboard
Redirect to child page trigger load() of parent again
Describe the bug
When redirecting to child page from parent page (that is not fully resolved), the load()
of parent layout is called twice.
For instance, start at root /
and try goto('/parent')
which should redirect to /parent/child-1
, then the load()
of parent/__layout
is called twice. When fetching data, it's called twice (fetching data twice) and blocking rendering while loading
.
└── parent/
├── __layout.svelte // load function is triggered twice
├── index.svelte // redirect to child-x
├── child-1/
│ ├── index.svelte
└── child-2/
├── index.svelte
// parent/index.svelte
<script context="module">
export async function load() {
return {
status: 302,
redirect: './parent/child-1'
};
}
</script>
Reproduction
https://stackblitz.com/edit/sveltejs-kit-template-default-9nhxwy?file=README.md
Repo has version that
- redirect from parent page to child page and
- redirect from parent layout to child page
but both versions trigger the load()
of parent twice.
Logs
No response
System Info
System:
OS: macOS 12.4
CPU: (10) arm64 Apple M1 Max
Memory: 103.23 MB / 32.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 16.14.2 - ~/.nvm/versions/node/v16.14.2/bin/node
Yarn: 1.22.17 - ~/.nvm/versions/node/v17.5.0/bin/yarn
npm: 8.5.0 - ~/.nvm/versions/node/v16.14.2/bin/npm
Browsers:
Chrome: 103.0.5060.53
Firefox: 102.0
Safari: 15.5
Safari Technology Preview: 16.0
npmPackages:
@sveltejs/adapter-auto: 1.0.0-next.52 => 1.0.0-next.52
@sveltejs/adapter-node: 1.0.0-next.78 => 1.0.0-next.78
@sveltejs/adapter-static: 1.0.0-next.34 => 1.0.0-next.34
@sveltejs/kit: 1.0.0-next.355 => 1.0.0-next.354
svelte: 3.48.0 => 3.48.0
Severity
serious, but I can work around it
Additional Information
No response
My understanding is that this is intended. The framework shouldn't make assumptions about how long data should be valid for. If the API responses have the appropriate cache headers, they will be cached.
That's a good solution but I'm using websockets and cannot cache the responses. Further code is also executed twice and I would like to avoid it.
I tried to check url.pathname
and only fetch when parent view is opened (the first time before redirect) but the props/stuff do not remain in layout after redirecting (second time) and I have to fetch the data again in the onMount of the layout component. Okay, that's not better than fetching the data twice in the load()
.
Currently I save the result in a store, but that's only working because my websockets are only clientside.
This leads me to the question:
- Is it possible to know the request to the page was triggered by a redirect?
-
Or would it be possible to pass
stuff
object when redirecting?
Then I could pass stuff
from layout to page, redirect with stuff
. When the layout is executed the second times I check for data in stuff
and pass to layout component.
/**
// /parent/__layout.svelte
<script context="module">
export async function load({ fetch, url, stuff }) {
// stuff is always empty
// only if called directly
if (url.pathname.endsWith('rollover')) {
const data = await fetchSomeData();
myStore.set(data);
return {
status: HttpStatusCode.OK,
props: {
// pass to layout (not available in resolved layout
// because second call does not end with /rollover and data are not fetched
data
},
stuff: {
// pass to view (/parent/index.svelte to redirect based on data
data
}
};
} else {
return {
status: 200,
// stuff does not persist data when redirecting
};
}
}
</script>
// /parent/index.svelte
<script context="module">
export async function load({stuff}) {
// sutff.data is available
return {
status: 302,
redirect: './parent/child',
// stuff do not remain after redirect, and is not available in layout's second execution
stuff: {
data: stuff.data
}
};
}
</script>
It does seem reasonable to reuse data nodes if there's overlap between the source and target (this isn't specific to parent-child relationships), though we need to be careful that things like params
remain valid.
This has gotten better with checking for reusability of data nodes, but in this case it will still load twice:
- goto(..)
- layout X is deemed as invalid, needing to fetch data again
- redirect happens further down the chain
- layout X is checked again, deemed as invalid again, because the comparison is made to the state before the navigation, so data is fetched a second time (essentially it is as if 2 never happened)
I'm not sure the additional complexity of keeping a list of pending updated nodes for possible reuse is worth it, especially considering things like invalidation, which could happen at the same time.