Search code examples
sveltesveltekit

Svelte store : work in layout but not in page


In a sveltekit application, I'm using the store to save a user email after success logged in. The store seems to work in the layout but not in pages. Can someone explain me what I'm doing wrong ? Moreover, I noticed that when I save the file in VS Code the HMR makes the store in +page.svelte work, but when I manually reload the page the store goes back to undefined. Thanks for your help !

Here's my stores.js file :

// lib/stores.js
import { writable } from 'svelte/store';

export const userEmail = writable(null);

Here's the layout where I manage the connection (with Firebase) and where I set the store value :

// +layout.svelte
<script>
    import { userEmail } from "$lib/stores.js";

    import { onMount } from "svelte";
    import { auth } from "$lib/firebase/fbInit";

    onMount(() => {
        auth.onAuthStateChanged(async (user) => {
            if (user) {
                await userEmail.set(user.email);
                console.log($userEmail + " is connected"); // ----- The store works here and shows the user email
            } else {
                console.log("no user connected");
            }
        });
    });
</script>

And here's the page example where I want to access the store value :

// examplepage/+page.svelte
<script>
    import { userEmail } from "$lib/stores.js";
    import { onMount } from "svelte";

    onMount(async () => {
        console.log("Email in store", $userEmail); // ----- But not working here : shows undefined (but the layout console.log still shows the right user email yet)
    });
</script>

Solution

  • This has nothing to do with the page/layout distinction.

    In the layout you do something when an event happens that actually influences the auth state.

    On the page you log once on page load. If the page loads before the auth state change happens, you will get nothing in the store.

    If you want to do something once the store has been updated, use a reactive statement like this, for example:

    $: if ($userEmail) {
      console.log('user e-mail is:', $userEmail);
    }
    

    Use in markup, e.g. in an {#if $userEmail} will be reactive as well.

    Also, you should avoid global stores (e.g. instances like this one that are exported directly from a module) if they can be evaluated during server-side rendering. The store would be shared between all requests and if it is no cleaned up correctly might contain state belonging to a different user.

    The same is of course true for any global state.

    Would recommend the use of a context instead, they can also include stores to transport reactivity.