Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

Turn params, query, preloading and page into stores #546

Closed
Rich-Harris opened this issue Jan 14, 2019 · 2 comments
Closed

Turn params, query, preloading and page into stores #546

Rich-Harris opened this issue Jan 14, 2019 · 2 comments

Comments

@Rich-Harris
Copy link
Member

Collective realisation in Discord today that preloading, which is currently a prop set on the top-level element when preloading starts, could be a reactive store instead with Svelte 3. That'd be more convenient than passing the prop around.

There might be other things that are best implemented as stores, but that's the only one that springs to mind

@Rich-Harris
Copy link
Member Author

Been thinking about this a little more as I work on the Svelte 3 version of Sapper. At the moment, we pass params, query and path around as props, which means you can do this sort of thing in components:

<script>
  export default {
    async oncreate() {
      const { params } = this.get();

      const res = fetch(`some-resource/${params.id}.json`);
      this.set({
        stuff: await res.json()
      });
    }
  };
</script>

The v3 equivalent would be

<script>
  import { onMount } from 'svelte';

  export let params;

  let stuff;

  onMount(async () => {
    const res = await fetch(`some-resource/${params.id}.json`);
    stuff = await res.json();
  });
</script>

If the user navigates from foo/bar to foo/baz (where the underlying route is foo/[id]), the component is destroyed and recreated, causing the oncreate (v2) or init code (v3) to run again.

But this approach causes problems like #349, because components aren't destroyed if the only thing that changes is the query string. The obvious way to fix that bug would be to recreate the entire tree, but that has costs. It also arguably takes some control away from the developer — it's much harder to do creative transitions between pages, for example.

There's a possible alternative. Rather than passing around things as props when they're clearly app-level concerns, we can use Svelte's canonical solution to this problem: stores.

Using stores, the example above might look like this:

<script>
  import { page } from '@sapper/app';

  let stuff;

  async function init({ id }) {
    stuff = null;
    stuff = await fetch(`some-resource/${id}.json`).then(r => r.json());
  }

  $: if (process.browser) init($page.params);
</script>

There's a lot of fairly advanced Svelte-specific stuff going on here. Users have to be aware that initialisation code only runs when the route is first visited, not whenever the page changes. That's a potential footgun. (The code above contains a race condition, for example.)

It also doesn't completely solve #349 unless preload refired for every query string change, for both layout and leaf components.

But it still feels like a more 'correct' and flexible solution, and even if the reactivity stuff is fairly advanced, it's still just Svelte. Would welcome any feedback and ideas.

@Rich-Harris Rich-Harris changed the title preloading store Turn params, query, preloading and page into stores Jan 31, 2019
@Rich-Harris
Copy link
Member Author

This is now mostly implemented on the svelte-3 branch — there are two stores, page and preloading, in addition to the per-request session = getSession() store. page value is a { path, params, query } object; preloading is a boolean.

I'm increasingly of the opinion that this is the right direction — aside from the performance benefits (from not destroying/recreating components unnecessarily) it'll enable transitions between sibling pages that would otherwise be prohibitively difficult.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant