Reader small image

You're reading from  Real-World Svelte

Product typeBook
Published inDec 2023
Reading LevelIntermediate
PublisherPackt
ISBN-139781804616031
Edition1st Edition
Languages
Right arrow
Author (1)
Tan Li Hau
Tan Li Hau
author image
Tan Li Hau

Tan Li Hau is a frontend developer at Shopee and a core maintainer of Svelte. He has delivered multiple conference talks and workshops on Svelte. Passionate about sharing his knowledge, Li Hau regularly contributes to the community through blog posts, YouTube videos, and books. He aspires to inspire others to explore and learn about Svelte and other modern web technologies.
Read more about Tan Li Hau

Right arrow

Reusing lifecycle functions in Svelte components

In the previous section, we learned that we can extract the calling of lifecycle functions into a function and reuse the function in other components.

Let’s look at an example. In this example, after the component is added to the screen for 5 seconds, it will call the showPopup function. I want to reuse this logic of calling showPopup in other components:

<script>
  import { onMount } from 'svelte';
  import { showPopup } from './popup';
  onMount(() => {
    const timeoutId = setTimeout(() => {
      showPopup();
    }, 5000);
    return () => clearTimeout(timeoutId);
  });
</script>

Here, I can extract the logic into a function, showPopupOnMount:

// popup-on-mount.js
import { onMount } from 'svelte';
import { showPopup } from './popup';
export function showPopupOnMount() {
  onMount(() => {
    const timeoutId = setTimeout(() => {
      showPopup();
    }, 5000);
    return () => clearTimeout(timeoutId);
  });
}

And now, I can import this function and reuse it in any component:

<script>
  import { showPopupOnMount } from './popup-on-mount';
  showPopupOnMount();
</script>

You may be wondering, why not only extract the callback function and reuse that instead?

// popup-on-mount.js
import { showPopup } from './popup';
export function showPopupOnMount() {
  const timeoutId = setTimeout(() => {
    showPopup();
  }, 5000);
  return () => clearTimeout(timeoutId);
}

Over here, we extract only setTimeout and clearTimeout logic into showPopupOnMount, and pass the function into onMount:

<script>
  import { onMount } from 'svelte';
  import { showPopupOnMount } from './popup-on-mount';
  onMount(showPopupOnMount);
</script>

In my opinion, the second approach of refactoring and reusing is not as good as the first approach. There are a few pros in extracting the entire calling of the lifecycle functions into a function, as it allows you to do much more than you can otherwise:

  • You can pass in different input parameters to your lifecycle functions.

    Let’s say you wish to allow different components to customize the duration before showing the popup. It is much easier to pass that in this way:

    <script>
      import { showPopupOnMount } from './popup-on-mount';
      showPopupOnMount(2000); // change it to 2s
    </script>
  • You can return values from the function.

    Let’s say you want to return the timeoutId used in the onMount function so that you can cancel it if the user clicks on any button within the component.

    It is near impossible to do so if you just reuse the callback function, as the value returned from the callback function will be used to register for the onDestroy lifecycle function:

    <script>
      import { showPopupOnMount } from './popup-on-mount';
      const timeoutId = showPopupOnMount(2000);
    </script>
    <button on:click={() => clearTimeout(timeoutId)} />

    See how easy it is to implement it to return anything if we write it this way:

    // popup-on-mount.js
    export function showPopupOnMount(duration) {
      let timeoutId;
      onMount(() => {
        timeoutId = setTimeout(() => {
          showPopup();
        }, duration ?? 5000);
        return () => clearTimeout(timeoutId);
      });
      return timeoutId;
    }
  • You can encapsulate more logic along with the lifecycle functions.

    Sometimes, the code in your lifecycle functions callback function does not work in a silo; it interacts with and modifies other variables. To reuse lifecycle functions like this, you must encapsulate those variables and logic into a reusable function.

    To illustrate this, let’s look at a new example.

    Here, I have a counter that starts counting when a component is added to the screen:

    <script>
      import { onMount } from 'svelte';
      let counter = 0;
      onMount(() => {
        const intervalId = setInterval(() => counter++, 1000);
        return () => clearInterval(intervalId);
      });
    </script>
    <span>{counter}</span>

    The counter variable is coupled with the onMount lifecycle functions; to reuse this logic, the counter variable and the onMount function should be extracted together into a reusable function:

    import { writable } from 'svelte/store';
    import { onMount } from 'svelte';
    export function startCounterOnMount() {
      const counter = writable(0);
      onMount(() => {
        const intervalId = setInterval(() => counter.update($counter => $counter + 1), 1000);
        return () => clearInterval(intervalId);
      });
      return counter;
    }

    In this example, we use a writable Svelte store to make the counter variable reactive. We will delve more into Svelte stores in Part 3 of this book.

    For now, all you need to understand is that a Svelte store allows Svelte to track changes in a variable across modules, and you can subscribe to and retrieve the value of the store by prefixing a $ in front of a Svelte store variable. For example, if you have a Svelte store named counter, then to get the value of the Svelte store, you would need to use the $counter variable.

    Now, we can use the startCounterOnMount function in any Svelte component:

    <script>
      import { startCounterOnMount } from './counter';
      const counter = startCounterOnMount();
    </script>
    <span>{$counter}</span>

I hope I’ve convinced you about the pros of extracting the calling of lifecycle functions into a function. Let’s try it out in an example.

Exercise 1 – Update counter

In the following example code, I want to know how many times the component has gone through the update cycle.

Using the fact that every time the component goes through the update cycle, the afterUpdate callback function will be called, I created a counter that will be incremented every time the afterUpdate callback function is called.

To help us measure only the update count of a certain user operation, we have functions to start measuring and stop measuring, so the update counter is only incremented when we are measuring:

<script>
  import { afterUpdate } from 'svelte';
  let updateCount = 0;
  let measuring = false;
  afterUpdate(() => {
    if (measuring) {
      updateCount ++;
    }
  });
  function startMeasuring() {
    updateCount = 0;
    measuring = true;
  }
  function stopMeasuring() {
    measuring = false;
  }
</script>
<button on:click={startMeasuring}>Measure</button>
<button on:click={stopMeasuring}>Stop</button>
<span>Updated {updateCount} times</span>

To reuse all the logic of the counter: – the counting of update cycles and the starting and stopping of the measurement – we should move all of it into a function, which ends up looking like this:

<script>
  import { createUpdateCounter } from './update-counter';
  const { updateCount, startMeasuring, stopMeasuring } = createUpdateCounter();
</script>
<button on:click={startMeasuring}>Measure</button>
<button on:click={stopMeasuring}>Stop</button>
<span>Updated {$updateCount} times</span>

The update counter returns an object that contains the updateCount variable and the startMeasuring and stopMeasuring functions.

The implementation of the createUpdateCounter function is left as an exercise to you, and you can check the answer at https://github.com/PacktPublishing/Real-World-Svelte/tree/main/Chapter01/01-update-counter.

We’ve learned how to extract a lifecycle function and reuse it, so let’s take it up a notch and reuse multiple lifecycle functions in the next pattern: composing lifecycle functions.

Previous PageNext Page
You have been reading a chapter from
Real-World Svelte
Published in: Dec 2023Publisher: PacktISBN-13: 9781804616031
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Tan Li Hau

Tan Li Hau is a frontend developer at Shopee and a core maintainer of Svelte. He has delivered multiple conference talks and workshops on Svelte. Passionate about sharing his knowledge, Li Hau regularly contributes to the community through blog posts, YouTube videos, and books. He aspires to inspire others to explore and learn about Svelte and other modern web technologies.
Read more about Tan Li Hau