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.