Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Real-World Svelte

You're reading from   Real-World Svelte Supercharge your apps with Svelte 4 by mastering advanced web development concepts

Arrow left icon
Product type Paperback
Published in Dec 2023
Publisher Packt
ISBN-13 9781804616031
Length 282 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Tan Li Hau Tan Li Hau
Author Profile Icon Tan Li Hau
Tan Li Hau
Arrow right icon
View More author details
Toc

Table of Contents (22) Chapters Close

Preface 1. Part 1: Writing Svelte Components
2. Chapter 1: Lifecycles in Svelte FREE CHAPTER 3. Chapter 2: Implementing Styling and Theming 4. Chapter 3: Managing Props and State 5. Chapter 4: Composing Components 6. Part 2: Actions
7. Chapter 5: Custom Events with Actions 8. Chapter 6: Integrating Libraries with Actions 9. Chapter 7: Progressive Enhancement with Actions 10. Part 3: Context and Stores
11. Chapter 8: Context versus Stores 12. Chapter 9: Implementing Custom Stores 13. Chapter 10: State Management with Svelte Stores 14. Chapter 11: Renderless Components 15. Chapter 12: Stores and Animations 16. Part 4: Transitions
17. Chapter 13: Using Transitions 18. Chapter 14: Exploring Custom Transitions 19. Chapter 15: Accessibility with Transitions 20. Index 21. Other Books You May Enjoy

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.

You have been reading a chapter from
Real-World Svelte
Published in: Dec 2023
Publisher: Packt
ISBN-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.
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 $19.99/month. Cancel anytime
Banner background image