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
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.
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.
Most helpful comment
This is now mostly implemented on the svelte-3 branch — there are two stores,
pageandpreloading, in addition to the per-requestsession = getSession()store.pagevalue is a{ path, params, query }object;preloadingis 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.