Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Svelte/Sapper.js - How to initialize store with localStorage data?

I come from a React background, but I'm switching to Svelte and Sapper for my next application in order to fight the massive bundle size that comes with React these days. However, I'm having trouble initializing Svelte's store with data retrieved from localStorage.

As per the Sapper docs (https://sapper.svelte.dev/docs#Getting_started), I created my project by running npx degit "sveltejs/sapper-template#rollup" my-app from the command line. I then installed the dependencies and removed the demo code in the src folder.

I then created two files: src/routes/index.svelte and src/store/index.js.

Code for both:

src/store/index.js

    import {writable} from "svelte/store";
    
    export let userLang;
    
    if(typeof window !== "undefined") {
        userLang = writable(localStorage.getItem("lang") || "en");
    } else {
        userLang = writable(null);
    }

src/routes/index.svelte

    <script>
        import {userLang} from "../store";
    </script>
    
    <p>Your Preferred Language: {$userLang}</p>

When I run the application and hit the index route, I see this:

Your Preferred Language: null

which then almost immediately updates and changes to

Your Preferred Language: en

when there is no lang item in localStorage, and changes to

Your Preferred Language: fr

After explicitly setting localStorage.setItem("lang", "fr") from the developer console and refreshing.

I know that the store is being initialized on the server first where window is undefined and then is being rehydrated on the client. So this behavior is expected.

So my question is: how can I skip the server initialization entirely? Is it possible to only set up the store on the client (where localStorage is defined) so that the user's chosen language is immediately available?

I can't default to having everything in English or any other language after the user has chosen to change their preferred language. I also can't get the user language from the browser via navigator.language on initial page load either since navigator is undefined on the server as well.

And having a flash of empty text appear before the store rehydrates would screw up the UX for my application, especially when the value of userLang is going to be used all over the place with translations.

So any strategies or hacks for this are definitely appreciated.

**** Deeper Issue ****

I would actually prefer to not have server-side rendering at all for this application, but I do need all the other excellent features that Sapper provides, like routing, prefetching, and static site building.

So I tried running npx sapper export as per the docs to generate a completely static site in an effort to remove the server from the equation, but the exact same issue still occurs, even though there is no server being used at all.

Does anyone have any advice on how to configure Sapper and turn off SSR but keep the other features?

Thank you!

**** Update ****

As per Rich Harris's answer, wrapping the markup with {#if process.browser} does the trick just fine. So I've updated the src/routes/index.sveltefile like so:

    <script>
        import {userLang} from "../store";
    </script>

    {#if process.browser}
        <p>Your Preferred Language: {$userLang}</p>
    {/if}

And the userLang variable is immediately set with the value from localStorage or defaults to en as I intended for this simple demo. There is no more flash of null, so it's essentially behaving like it's client-side only at this point.

I will work on fleshing out my project and see if there are any more issues I encounter. Til then, I think this solves my issue.

like image 520
another_one Avatar asked Jun 17 '19 18:06

another_one


People also ask

How do I use localStorage in svelte?

A simple solution is to save these variables to the underlying browser's localStorage . Let's modify the store to retrieve the default value from localStorage . import { writable } from "svelte/store"; const storedTheme = localStorage. getItem("theme"); export const theme = writable(storedTheme);

How do you persist shop in svelte?

You can manually create a subscription to your store and persist the changes to localStorage and also use the potential value in localStorage as default value. @AnilSivadas Doing it on the server complicates it a bit. You could skip it on the server and just do it in the browser with a typeof window !==

Are Svelte stores reactive?

Going over it again, Svelte stores are basically just reactive Javascript objects which can be imported into multiple components, all unrelated to each other. The examples used here are very simple, and it is likely that you will be using Svelte stores for more complicated data, but the concepts will remain the same.

What is store in Svelte?

Svelte stores offer similar features for state management. A store is an object with a subscribe() method that allows interested parties to be notified whenever the store value changes, and an optional set() method that allows you to set new values for the store. This minimal API is known as the store contract.


1 Answers

At present, SSR is non-optional. There's an issue open for an SPA mode — https://github.com/sveltejs/sapper/issues/383 — that would behave as you describe, we just need to get round to it.

We also plan to have built-in support for i18n in a future release: https://github.com/sveltejs/sapper/issues/576

In the meantime, you can fake it by wrapping all your markup in {#if process.browser} — anything inside won't be server rendered, but will be present as soon as JavaScript kicks in.

like image 94
Rich Harris Avatar answered Oct 05 '22 17:10

Rich Harris