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

Fix the content flash problems with contexts #2

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

kevinrenskers
Copy link
Owner

@kevinrenskers kevinrenskers commented Aug 16, 2021

The main key takeaway from the problems I was seeing with the content (on refresh it flashes from old content to new content, and the old content is shared between browsers), is that using Svelte stores from SSR is a hard no. That state is shared across all clients, so it's just not usable.

The initial fix to simply add a check to see if we're in the browser wasn't ideal, as you'd now see content flash from "undefined" to the actual content (see also this article). By adding contexts and using either the store or the context as a fallback, I've solved all the content problems, but at the cost of considerable boilerplate.

The problem where after login the header wasn't updated also fixed itself.

@mekanix
Copy link

mekanix commented Aug 30, 2021

I tried to learn as much as I could from your issue, this PR and blog post and I have to ask one thing. Why is ssr = false not a solution? As somebody who's just starting with Svelte I'm not trying to state anything, it's just a honest question.

@kevinrenskers
Copy link
Owner Author

SSR is kind of the whole point of using SvelteKit. It massively reduces the time to load the initial page: instead of serving a blank HTML page and then the needed content is loaded from the browser (thus the user seeing a bunch of spinners and other loading indicators). With SSR the server page is already fully complete, nothing needs to be loaded asynchronously anymore.

@mekanix
Copy link

mekanix commented Aug 30, 2021

Ah, so ssr = false would work but it's not what you're after? OK, now it makes sense. I'll have to read the code more carefully (it's what I'm doing right now) in order to understand the solution. Anyway, thank you for explanation and code!

@kevinrenskers
Copy link
Owner Author

Right, without SSR you could always use a writable store and have none of the problems.

@AlbertMarashi
Copy link

Other frameworks sandbox each server-side render of apps. Using the node VM Module
https://nodejs.org/api/vm.html

@kurtie
Copy link

kurtie commented Mar 3, 2022

Today I've learned about this issue the hard way: When using SSR, you have two stores, one on client and server. if you hit F5, server store remembers old values while client one is reset as I would expect:

This code is on some svelte page:

import { count } from '$lib/store.ts';
count.update(n => n+1)
console.log("count", $count)

The store is a simple: export const count = writable(0);

Now if I execute this, and do F5 on the page, on svelte-kit console I see "count 1, count 2, count 3...", but on browser console "count 1, count 1, count 1..." which is the desired behavior on a full page reload.

The point of SSR is to execute the same code on server and on client, and this breaks coherence between server and client, making SvelteKit SSR quite limited.

Any plans to fix this? Would work if each SSR request had its own stores?

@kevinrenskers
Copy link
Owner Author

Any plans to fix this?

That's probably best asked on the Svelte repo :)

@kevinrenskers
Copy link
Owner Author

Please check out https://www.loopwerk.io/articles/2022/sveltekit-architecture/ and https://github.com/loopwerk/sveltekit-architecture for a much better version of this solution.

@xsonic
Copy link

xsonic commented Jun 17, 2022

Thanks for your write ups, this helped me a great deal in understanding how Svelte (and SSR in general) works.

I ran into the same problem and came up with a similar solution. But I simply set the store on the client, and not in the load function. I don't know if I am missing something, but it seems to work and the API is only hit once.

<pre>{JSON.stringify(pokemon)}</pre>

<script lang="ts">
    import {pokeStore} from "../stores/general";

    export let pokemon
    $: pokemon && pokeStore.set(pokemon)
</script>

<script lang="ts" context="module">
    import {browser} from "$app/env"
    import {get} from "svelte/store";
    import {pokeStore as pokeStoreSS} from "../stores/general";

    export async function load({fetch}) {

        let pokemon

        if (browser && get(pokeStoreSS).length) {
            pokemon = get(pokeStoreSS)
        } else {
            const response = await fetch('https://pokeapi.co/api/v2/pokemon?limit=10')
            pokemon = (await response.json()).results
        }

        return {
            props: {
                pokemon: pokemon
            }
        }
    }
</script>

@kevinrenskers
Copy link
Owner Author

But you're always doing pokeStore.set, also in server side rendering. Which then breaks things.

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

Successfully merging this pull request may close these issues.

5 participants