Welcome to the first lesson of the course Svelte: Reactivity & State Management! In this lesson, we’ll dive into one of the most powerful features of Svelte: reactive side effects. Reactive side effects are actions that happen automatically when your application’s state changes. For example, if you’re building a countdown timer, you might want to update the timer every second. This is where the $effect
rune comes in. It allows you to manage these side effects in a clean and efficient way.
Think of $effect
as a way to say, “Whenever this value changes, do this.” It’s like setting up a watchman who keeps an eye on specific data and takes action when it changes. In this lesson, we’ll use $effect
to build a countdown timer, and you’ll learn how to handle side effects like timers, event listeners, and more.
By the end of this lesson, you’ll understand how to use $effect
to manage side effects and ensure your application runs smoothly. Let’s get started!
The $effect
rune is a key part of Svelte’s reactivity system. It allows you to run code whenever certain values change. Here’s a simple example to illustrate how it works:
svelte1<script> 2 let count = $state(0); 3 4 $effect(() => { 5 console.log(`Count is now: ${count}`); 6 }); 7</script> 8 9<button onclick={() => count++}>Increment</button>
In this example, every time the count
variable changes, the $effect
rune logs the new value to the console. The $effect
rune automatically tracks dependencies, so you don’t need to manually specify which values to watch. This makes your code cleaner and easier to maintain.
The $effect
rune is especially useful for managing side effects like timers, event listeners, or API calls. In the next section, we’ll explore how to use $effect
for cleanup to prevent memory leaks or unwanted behavior.
When using $effect
, it’s important to clean up after yourself to avoid memory leaks or unexpected behavior. For example, if you set up a timer using setInterval
, you should clear it when it’s no longer needed. Here’s how you can do that:
svelte1<script> 2 let timeLeft = $state(10); 3 let running = $state(false); 4 5 $effect(() => { 6 if (!running) return; 7 8 const id = setInterval(() => { 9 if (timeLeft > 0) { 10 timeLeft -= 1; 11 } else { 12 running = false; 13 } 14 }, 1000); 15 16 return () => { 17 clearInterval(id); // Cleanup the interval 18 }; 19 }); 20</script> 21 22<button onclick={() => running = true}>Start</button> 23<button onclick={() => running = false}>Stop</button> 24<p>Time left: {timeLeft}</p>
In this example, the $effect
rune sets up a timer that counts down from 10. When the timer is no longer needed (e.g., when the user clicks “Stop”), the cleanup function (clearInterval
) is called to stop the timer. This ensures that your application doesn’t waste resources or behave unexpectedly.
Cleanup is a crucial part of using $effect
, and it’s something you’ll need to consider whenever you’re managing side effects. In the next section, we’ll explore how to use the untrack
function to avoid unnecessary re-runs.
Sometimes, you might want to run code inside $effect
without tracking dependencies. This is where the untrack
function comes in. It allows you to run code without triggering a re-run of the $effect
when the values change. Here’s an example:
svelte1<script> 2 import {untrack} from "svelte" 3 let timeLeft = $state(10); 4 let running = $state(false); 5 let startCount = $state(0); 6 7 $effect(() => { 8 if (!running) return; 9 10 const id = setInterval(() => { 11 if (timeLeft > 0) { 12 timeLeft -= 1; 13 } else { 14 running = false; 15 } 16 }, 1000); 17 18 return () => { 19 clearInterval(id); 20 }; 21 }); 22 23 $effect(() => { 24 if (running) { 25 untrack(() => { 26 startCount++; 27 }); 28 } 29 }); 30</script> 31 32<button onclick={() => running = true}>Start</button> 33<button onclick={() => running = false}>Stop</button> 34<p>Time left: {timeLeft}</p> 35<p>Total starts: {startCount}</p>
In this example, the untrack
function is used to increment the startCount
variable without triggering a re-run of the $effect
. This is useful when you want to perform an action that doesn’t depend on the reactive state.
By combining $effect
with untrack
, you can create more efficient and predictable code. In the next section, we’ll put everything together to build a complete countdown timer.
Now that you understand the basics of $effect
and untrack
, let’s build a complete countdown timer. Here’s the code we’ll be working with:
svelte1<script> 2 let timeLeft = $state(10); 3 let running = $state(false); 4 let startCount = $state(0); 5 6 $effect(() => { 7 if (!running) return; 8 9 const id = setInterval(() => { 10 if (timeLeft > 0) { 11 timeLeft -= 1; 12 } else { 13 running = false; 14 } 15 }, 1000); 16 17 return () => { 18 clearInterval(id); 19 }; 20 }); 21 22 $effect(() => { 23 if (running) { 24 untrack(() => { 25 startCount++; 26 }); 27 } 28 }); 29</script> 30 31<h3>Countdown Timer</h3> 32<button onclick={() => running = true}>Start</button> 33<button onclick={() => running = false}>Stop</button> 34<button onclick={() => { timeLeft = 10; running = false; }}>Reset</button> 35<p>Time left: {timeLeft}</p> 36<p>Total starts: {startCount}</p>
This code creates a countdown timer that starts at 10 seconds. The $effect
rune manages the timer, and the untrack
function increments the startCount
variable without triggering a re-run. The timer can be started, stopped, and reset using the buttons.
When you run this code, you’ll see the timer count down from 10, and the “Total starts” counter will increase each time you start the timer. This example demonstrates how to use $effect
and untrack
together to manage side effects and state in a Svelte application.
Below is a preview of how the timer will look when running:
Using $effect
efficiently ensures that your Svelte applications remain performant and free of common pitfalls. Follow these best practices to manage reactive side effects effectively:
- Use
$effect
only for side effects. Avoid using it for derived values; use $derived instead. - Always return a cleanup function. When using
setInterval
, event listeners, or subscriptions, clean them up to prevent memory leaks. - Prevent infinite loops with
untrack()
. If modifying a reactive variable inside$effect
, wrap the change withuntrack()
to avoid unnecessary re-runs. - Keep
$effect
logic simple. Break large effects into smaller, focused ones for better maintainability. - Avoid unnecessary
$effect
statements. If a $state or $derived can achieve the same result, use that instead for better performance.
By following these best practices, you’ll write cleaner, more efficient Svelte code while avoiding reactivity pitfalls. 🚀
In this lesson, you learned how to use the $effect
rune to manage reactive side effects in Svelte. We covered the basics of $effect
, how to use it for cleanup, and how to combine it with untrack
to avoid unnecessary re-runs. You also built a functional countdown timer using these concepts.
Here are the key takeaways:
- Use
$effect
to run code whenever specific values change. - Always clean up after side effects (e.g., clear intervals or unsubscribe from events) to prevent memory leaks.
- Use
untrack
to run code without tracking dependencies, making your code more efficient.
In the next lesson, we’ll explore more advanced topics in Svelte’s reactivity system. For now, try experimenting with the countdown timer code and see how you can modify it to add new features. Great job on completing this lesson—you’re well on your way to mastering Svelte!