App.svelte listener to store

how can I add a listener to a value from the store in the App.svelte file?(where the App is declared)
My current solution which gives the error “Uncaught Error: Function called outside component initialization”:

    <App themeDark={themeDark}></App>
    <script>
import { onMount } from "svelte";
  import { f7, f7ready, App, View, useStore } from "framework7-svelte";
$:themeDark=false;
        onMount(() => {
        f7ready(() => {

        setTimeout(() => {
                themeDark = useStore("themeDark", (val) => {
                  themeDark = val;
                });
              }, 100);

        })
        })
    </script>

2 questions:

  1. Why are you not using proper svelte stores instead of useStore?
  2. Why are you using f7ready inside onMount, infact why are you using f7ready at all in a svelte component?

Also this line: $:themeDark=false; in the best scenario it’s going to do literally nothing and it the worst scenario it’s going to make sure that your variable is ALWAYS false, because that reactive statement technically has the variabile themeDark itself as a dependency.

If it weren’t for svelte that stops infinite loops, that would be a case of an infinite loop.


Make a new file ./stores/themeDark.js and throw this in it:

import { writable } from 'svelte/store';

const themeDark= writable(false);

export default themeDark;

And this is what your entry component should look like:

<script>
import { App } from "framework7-svelte";
import themeDark from "./stores/themeDark";

$:onThemeDarkChange($themeDark);
function onThemeDarkChange(value){
  console.log("themeDark has changed, its new value is:",value);
}
</script>

<App themeDark={$themeDark}></App>

You don’t even need the reactive statement, as long as they are used within a .svelte file, stores and local variables are reactive by nature, infact the whole component will run just fine like this:

<script>
import { App } from "framework7-svelte";
import themeDark from "./stores/themeDark";
</script>

<App themeDark={$themeDark}></App>

The store will update the App component automatically.

Stores follow the Observer design pattern, so you can subscribe to them outside a svelte file using pure javascript aswell, like so:

//hypothetical "someModule.js" file
import themeDark from "./stores/themeDark";
const unsubscribe = themeDark.subscribe(value=>{
   console.log("themeDark has changed:",value);
});

This way you can listen for changes.
If you simply want to read its value once (using vanilla js), you just call the unsubscribe function right away:

//hypothetical "someModule.js" file
import themeDark from "./stores/themeDark";
let _themeDark;
const unsubscribe = themeDark.subscribe(value=>{
   _themeDark = value;
});
unsubscribe();

or call it inline:

/hypothetical "someModule.js" file
import themeDark from "./stores/themeDark";
let _themeDark;
themeDark.subscribe(value=>{
   _themeDark = value;
})();
console.log(_themeDark);

Regardless, when using stores within components you must remember to unsubscribe when components are destroyed, so this method is not recommended while within svelte components.

That’s why the $ shorthand exist.
When used it will automatically:

  1. subscribe to the store
  2. get the value for you
  3. update every object that depends on that store when its value changes
  4. and most importantly unsubscribe from the store when components are destroyed

I suggest you go through the official tutorial of svelte stores: https://svelte.dev/tutorial/writable-stores

1 Like

using useStore is literally what the documentation says to do, and how its done in the standard app template generated when creating a F7 svelte app.

So is the documentation wrong, or are you? Because now I’m even more confused.