Using sveltekit with svelte 4,
There's data required to server side render pages that is loaded in the root +layout.server.js
. The data is then used both on the client and server side, however, I need a way to edit the data after the page is loaded and the $page
store is readonly. If I import a store and .set() it inside the root +layout.svelte
, then all users/requests will trigger updates to the store on the server side, which is bad.
So how do I go about creating a store, or simple object, that is instantiated in the root server layout, which can be edited on the client side later?
I tried to use depends('data:rates')
but that makes all the nested routes rerun their load functions and it still doesn't let me have fine-grained control on the data and timing from the client.
Note that a store limited to one file would work, but this store/object needs to be imported by dozens of locations across the site.
Thanks.
Assuming your +layout.server.js
looks something like this:
export const load = async ({ fetch }) => {
const response = await fetch(`/api/rates`);
const rates = await response.json();
return { rates: rates }
}
Two ways to solve this problem in my experience:
In an adjacent client-side +layout.js
, return a writable:
import { writable } from 'svelte/store';
export const load = async ({ parent, data }) => {
return {
...(await parent()),
...data,
rates: writable(data.rates),
};
};
Child pages can use the store with data.rates
<script>
// +page.svelte
export let data;
$: rates = data.rates;
// or; in other components
// import { page } from '$app/stores'
// $: rates = $page.data.rates;
async function refreshRates = () => {
$rates = await fetch(`/api/rates`);
}
</script>
<button on:click={refreshRates}>Refresh</button>
This example is also given in the Svelte docs:
<script>
import { onMount, setContext } from 'svelte';
import { writable } from 'svelte/store';
export let data;
const rates = writable(null);
$: setContext('rates', rates);
$: rates.set(data.rates);
</script>
<slot/>
Then, in a child page/component:
<script>
import { setContext } from 'svelte';
const rates = getContext('rates', rates);
async function refreshRates = () => {
$rates = await fetch(`/api/rates`);
}
</script>
<button on:click={refreshRates}>Refresh</button>