Home > Blockchain >  Svelte Auto Subscription in OnMount
Svelte Auto Subscription in OnMount

Time:12-08

I have a simple Svelte project that uses a store to hold shared data across components. Part of this project also references HTML elements (for example a username input field).

Reading the documentation here and here, I understood that I need to:

  • Bind an HTML element in the onMount method
  • I can use auto subscription ($) instead of manual subscription and not have to deal with unsubscribing through the onDestroy method

At first, I used manual subscription and everything worked well. When I moved to automatic subscription, it did not work and the values didn't get updated.

Below is a minimal example with automatic subscription:

Spinner.svelte

<script lang="ts">
    import { shouldShowSpinner } from './AppStore';
    import { onMount} from 'svelte';

    let spinnerDiv
  

    onMount(() => {
        spinnerDiv.style.display = $shouldShowSpinner ? "inline-block" : "none"
      })

   
</script>

<main>
   <div id="spinner" bind:this={spinnerDiv}>
   </div>
</main>

UserInput.svelte:

<script lang="ts">
    import { shouldShowSpinner } from './AppStore';

    let username = "";

    function foo() {
        if (username.length === 0) {
            return;
        }

        $shouldShowSpinner = true
        ...
    }

   
</script>

<main>
    <div id="userInput">
        <input id="username" type="text" placeholder="Username?" bind:value={username}/> 
        <button id="search" on:click={foo}>Search</button>
     </div>
</main>

Is there something that I am doing wrong here?

CodePudding user response:

First of all, you should generally not need to access elements. If you use idiomatic Svelte, the problem takes care of itself here.

onMount only runs once. Auto-subscription does not mean that the code will re-run, it just means that the store content will be accessed without leaking a subscription. When/how code runs still follows reactivity rules.

To fix the code, you would need a reactive statement like this:

$: if (spinnerDiv) spinnerDiv.style.display = ...

But that is not how to do this. Instead, just use {#if}, toggle a class or change the style in the template:

{#if $shouldShowSpinner}
   <div>...
{/if}
<div  class:show={$shouldShowSpinner}>...
<style>
  .spinner { display: none; }
  .spinner.show { display: inline-block; }
</style>
<div style:display={$shouldShowSpinner ? "inline-block" : "none"}>...

No need to use bind:this at all.

  • Related