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

Forward __layout props to pages #2250

Closed
kevinrenskers opened this issue Aug 20, 2021 · 9 comments
Closed

Forward __layout props to pages #2250

kevinrenskers opened this issue Aug 20, 2021 · 9 comments
Labels
feature request New feature or request router

Comments

@kevinrenskers
Copy link

kevinrenskers commented Aug 20, 2021

Describe the problem

I'm running into the situation where I have multiple pages all needing access to the same content, and I need to copy boilerplate into all those pages to make that possible.

I have a specific example: I have a whole bunch of pages (/campaigns/[id]/characters, /campaigns/[id]/locations, /campaigns/[id]/quests plus more and all of their subpages too), all of which need access to the campaign object which I get from a remote server. I currently have my system setup so that the actual fetching of the campaign from the API server is done in a central place: /campaigns/[id]/__layout.svelte. I then have logic to write that fetched campaign to a Svelte store if we're running in the browser, or return it as part of the context if we're in SSR. And before fetching from the remote server, I first check if we already have it cached in the store, to prevent duplicate fetches (which are otherwise very frequently happening). Something like this:

<script context="module">
  import { browser } from "$app/env";
  import { get } from "svelte/store";
  import { content } from "$lib/store";

  export async function load({ page, fetch, session }) {
    // If we already have the content, then don't fetch it again
    const storedContent = get(content);
    if (browser && storedContent) {
      return {
        props: { fetchedContent: storedContent }
      };
    }

    // Fetch remote content, give it back as a prop AND context
    const res = await fetch("my remote server url");
    const fetchedContent = await res.json();

    return {
      props: { fetchedContent },
      context: { fetchedContent }
    };
  }
</script>

<script>
  export let fetchedContent;

  // If we're running in the browser, then we can save the fetched content in the store
  if (browser) {
    $content = fetchedContent;
  }
</script>

Every single page then needs this kind of logic to either get the campaign from the store, or to fallback to the freshly fetched object in the context. That way we're using a reactive store in the browser and we can update the object in real time, and when we're running in SSR the store isn't used, but the context is.

<script context="module">
  // Forward context to props
  export async function load({ context }) {
    return { props: context };
  }
</script>

<script>
  import { content as contentStore } from "$lib/store";
  export let fetchedContent;

  // Use the content in the store, but fallback to the context, which is needed for SSR.
  $: content = $contentStore || fetchedContent;
</script>

(I've used "content" instead of "campaign" in these examples since they are somewhat simplified examples)

This works does work fine. I can prevent duplicate fetches (for example when you from from /about to /campaigns/1 to /about and back to /campaigns/1 normally that would result in a new fetch. Or when you go to one of the campaign subpages for the first time in a session, that normally also does a fetch, but my system prevents that, which is great (and important).

The downside is that it's annoying to keep copying this boilerplate to literally every single page where I need access to the campaign object.

Describe the proposed solution

It would be so great to be able to set props on the slot inside of the layout, like this:

// __layout.svelte
<slot {campaign} />

And all the pages could simply do export let campaign and they would have access to the campaign object. All the complex logic of fetching the campaign is in a single place, and we'd forward the resulting object to the page via that slot.

Alternatives considered

No response

Importance

would make my life easier

Additional Information

I have a project with a minimal example where I recreated my current setup so you can play around with it and see the boilerplate involved: https://github.com/kevinrenskers/sveltekit-reproduce/tree/fix/context.

@dummdidumm
Copy link
Member

Duplicate of #627

@dummdidumm dummdidumm marked this as a duplicate of #627 Aug 20, 2021
@kevinrenskers
Copy link
Author

I don't think it is a duplicate, that seems to be about named slots?

@dummdidumm
Copy link
Member

I read your feature request as "I want to be able to pass named slots from __layout to pages", which is the same as the linked issue.

@kevinrenskers
Copy link
Author

No; I want to pass props, I think that is different, right?

@dummdidumm
Copy link
Member

Ok I think I understand, that use case is slightly different, reopening. Although I'll say that the proposed solution doesn't seem sound with the rest, and to some extend conflicts with #627 . I think a better solution would be to either automatically pass down all props from the returned load, or introduce a new property like pageProps.

@dummdidumm dummdidumm reopened this Aug 20, 2021
@dummdidumm dummdidumm added feature request New feature or request router labels Aug 20, 2021
@kevinrenskers
Copy link
Author

kevinrenskers commented Aug 20, 2021

True, passing down all props would be a very handy solution too! I've renamed this issue to have less focus on the slot.

@kevinrenskers kevinrenskers changed the title Forward __layout slot props to pages Forward __layout props to pages Aug 20, 2021
@Acmion
Copy link

Acmion commented Nov 7, 2021

This could also enable inheritance of hydrate and router (and maybe others).

Currently one can choose between disabling the settings for all routes or specifying it on a per route basis.

@impactvelocity
Copy link

I used https://kit.svelte.dev/docs#loading-output-stuff in the main layout to add stuff to the stuff, and then I use it in sub pages / layouts to get stuff from 'stuff'. But it still requires you to do a load, and pass props down.

Adding props in the slot would be great to get rid of the load function, but it works!

@Rich-Harris
Copy link
Member

This is what stuff is for. You don't need to use a load function to access it, since it's available in the page store:

import { page } from '$app/stores';

$: console.log($page.stuff);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request router
Projects
None yet
Development

No branches or pull requests

5 participants