javascriptfrontendsvelte

Is using a dummy variable a good practice to trigger a function call reactively in Svelte


My code computes the number of characters the user has input. I allows users to save the entered text to localstorage and then when they visit the page the character count is shown. This is how I load the data:

// App.Svelte
<script>
  import Description from "./lib/Description.svelte";
  import Save from "./lib/Save.svelte";
  import { description, extraSections } from "./lib/store";
  import { onMount } from "svelte";

  onMount(() => {
    if (localStorage.getItem("extraSections") != null) {
      $extraSections = JSON.parse(localStorage.getItem("extraSections"));
    }
    $description = localStorage.getItem("description");
  });
</script>

<div class="container">
  <div class="inputs" id="inputs">
    <Title />
    <Description bind:text={$description} />
    {#each $extraSections as section (section.id)}
      <Description heading="Additional Section" bind:text={section.value} />
    {/each}
...

The problem I was facing was that my Description component wasn't showing the count for the text entered from the localstorage since my function to compute that ran only on keyup:

// Description.Svelte
<script>
  let count = 0;
  let words = 0;
  function handleKeyUp() {
    count = text.length;
    words = text.trim().split(/\s+/).length;
    if (text.length == 0) {
      words = 0;
    }
  }
  export let text = "";
  export let heading = "Description";
</script>

<div>
  <label for="title">{heading}</label>
  <textarea
    on:keyup={handleKeyUp}
    id="description"
    name="description"
    bind:value={text}
  ></textarea>
  <p>Characters: {count}</p>
  <p>Words: {words}</p>
</div>

To fix this I did this made the handleKeyUp function reactive to a change in value of text using the $: syntax:

<script>
  let count = 0;
  let words = 0;
  function handleKeyUp(dummy) {
    count = text.length;
    words = text.trim().split(/\s+/).length;
    if (text.length == 0) {
      words = 0;
    }
  }
  // whenever text changes (when loading from localstorage)
  // we want to rerun handleKeyUp
  $: handleKeyUp(text);
  export let text = "";
  export let heading = "Description";
</script>

I wanted to ask if this is a valid practice in Svelte? Or is there a better way to do this? Thanks!


Solution

  • This adds noise and can be confusing, so I would not recommend it.
    The most common approach in Svelte 3/4 is to use a sequence expression instead:

    $: text, handleKeyUp();
    

    But I would generally recommend defining the function reactively, so the dependencies in the function body are captured:

    $: handleKeyUp = () => {
      // ...
    };
    $: handleKeyUp();
    

    (Neither approach is necessary in Svelte 5, where dependencies are tracked across function boundaries at runtime.)