kit icon indicating copy to clipboard operation
kit copied to clipboard

Redirect to child page trigger load() of parent again

Open philippone opened this issue 2 years ago • 4 comments

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

philippone avatar Jun 28 '22 20:06 philippone

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.

Conduitry avatar Jun 28 '22 21:06 Conduitry

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>

philippone avatar Jul 05 '22 21:07 philippone

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.

Rich-Harris avatar Sep 20 '22 20:09 Rich-Harris

This has gotten better with checking for reusability of data nodes, but in this case it will still load twice:

  1. goto(..)
  2. layout X is deemed as invalid, needing to fetch data again
  3. redirect happens further down the chain
  4. 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.

dummdidumm avatar Sep 21 '22 10:09 dummdidumm