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

Using a Svelte store in the load function causes weird behavior #2213

Closed
kevinrenskers opened this issue Aug 15, 2021 · 4 comments
Closed

Using a Svelte store in the load function causes weird behavior #2213

kevinrenskers opened this issue Aug 15, 2021 · 4 comments

Comments

@kevinrenskers
Copy link

kevinrenskers commented Aug 15, 2021

Describe the bug

I'm building a SvelteKit web app with an external REST server, and I've come up with a solution to minimize the amount of requests that are done to the REST server, and to allow content to be updated in real time.

The idea is that the list view (let's say /books) fetches all books from the server, and stores it in a Svelte store. Then /books/[id] can just read the Svelte store, and if that book is part of the stored content, it can show it immediately, no fetch needed. But if it's not part of the array of books, it can fetch that single book from the REST server and append it to the Svelte store. All pages use the Svelte store to show the books, so any updates to the store automatically update the pages. The content can be changed via websocket updates - when they come in, I can just update the Svelte store, all pages updates, everything is great!

However, when I reload the page in the browser, I am seeing the old version of the content show up very briefly, after which it updates to the new version of the content. It looks pretty weird and buggy and I have absolutely no idea how to work around it.

I think it's related to how SSR works: it seems to store my Svelte store server-side on the initial page load, but after that when the content in the store changes, and I refresh the browser, it first loads that very first stored content before updating it in the browser with the latest version. It might simply be a huge mistake to even try to use Svelte stores in a load function, but in that case I wonder how else I am supposed to architect my app.

Reproduction

I've made a minimal, reproducible example: https://github.com/kevinrenskers/sveltekit-reproduce. If you run this one and refresh the page, you'll see content flashes.

If you keep an eye on the terminal and the console in the browser, you will see that the first time you open the page, the content is fetched first from SSR, and then from the browser too. But when you refresh the page, the fetching is only done in the browser.

System Info

System:
    OS: macOS 12.0
    CPU: (12) x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
    Memory: 7.43 GB / 32.00 GB
    Shell: 3.3.1 - /usr/local/bin/fish
  Binaries:
    Node: 16.5.0 - /usr/local/bin/node
    Yarn: 1.22.10 - /usr/local/bin/yarn
    npm: 7.19.1 - /usr/local/bin/npm
  Browsers:
    Brave Browser: 92.1.28.105
    Firefox: 91.0
    Safari: 15.0
  npmPackages:
    @sveltejs/kit: next => 1.0.0-next.144 
    svelte: ^3.34.0 => 3.42.1 

Severity

serious, but I can work around it

Additional Information

I'm not sure how to answer the severity question. It seems like a serious issue, I can NOT work around it, but perhaps it's just me holding it wrong? Let me put it like this: the way I've built the app at the moment, things just look buggy and weird when you refresh the browser.

@Conduitry
Copy link
Member

This is the intended behavior. As mentioned in the docs:

Mutating any shared state on the server will affect all clients, not just the current one.

For other usage and architecture questions, please ask on Discord or Stack Overflow.

@kevinrenskers
Copy link
Author

kevinrenskers commented Aug 16, 2021

For those interested: I have found a way to fix problems: kevinrenskers/sveltekit-reproduce#2, but it comes with considerable boilerplate. I'd really love it if SvelteKit could somehow make this a lot nicer. I was actually surprised how complex it was to do something that feels quite fundamental to building a SvelteKit app: fetching content, saving it in a store, and using that store from pages, allowing things like WebSockets to update the store and all pages update.

Update: I've come up with a better solution with almost no boilerplate, please see https://www.loopwerk.io/articles/2022/sveltekit-architecture/ and the example repo https://github.com/loopwerk/sveltekit-architecture. Hope this helps anyone!

@gterras
Copy link

gterras commented Sep 1, 2021

Mutating any shared state on the server will affect all clients, not just the current one.

@Conduitry Are there other "shared states" types than stores in this case?

@nicomouss
Copy link

nicomouss commented Jan 15, 2022

@kevinrenskers I just came from the same process of understanding how stores work server-side in SSR context. In my opinion it is better to deal with it instead of incorporating some boilerplate code.

Main thing to understand is that as soon as you create your own store, it becomes global server-side in a SSR context (= your store is a singleton in memory server-side, so it is shared by all HTTP requests hitting your server).

@Conduitry, I guess that should be made clearer in SvelteKit documentation. There is this below stated is the doc:

Mutating any shared state on the server will affect all clients, not just the current one.

but it is "hidden" in the section about load functions, and it's not clearly explaining that a store is global in a SSR context.

@kevinrenskers @gterras that is not intuitive stuff to understand I agree, because in a client-side environment, a store is contextual to each instance of your app (= each browser tab you open your app in). And then this exact same store is at the same time global to all app instances when in a server-side environment (in effect like any objects you might export from any module, which will be global instances server-side in SSR mode).

I guess an updated documentation would help to understand the way it works, and how we can deal with it: for example using onMount function, making sure we never mutate any server-side stores.

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

No branches or pull requests

4 participants