Exercises
Six Svelte exercises that surface the idioms most teams get slightly wrong. Try first; answers below.
Six Svelte drills
EXAMPLE
# ============================================================
# Drill 1 — Reactive mutation
# ============================================================
# A counter does not update when you click. Why?
# <script>
# const state = { count: 0 };
# function inc() { state.count++; }
# </script>
# <button on:click={inc}>{state.count}</button>
#
# ANSWER: Svelte tracks ASSIGNMENT, not mutation. Reassign the object:
# function inc() { state = { ...state, count: state.count + 1 }; }
# Or move to a writable store + .update().
# ============================================================
# Drill 2 — Derived store
# ============================================================
# You have writable stores 'cart' and 'taxRate'. Build a 'total' store
# that recomputes whenever either changes.
#
# ANSWER:
import { writable, derived } from 'svelte/store';
export const cart = writable([]);
export const taxRate = writable(0.10);
export const total = derived([cart, taxRate], ([$cart, $taxRate]) =>
$cart.reduce((s, i) => s + i.priceCents, 0) * (1 + $taxRate));
# ============================================================
# Drill 3 — Action with cleanup
# ============================================================
# Write a 'use:clickOutside' action that calls back when the user clicks
# outside the element.
#
# ANSWER:
export function clickOutside(node, callback) {
function handle(e) { if (!node.contains(e.target)) callback(e); }
document.addEventListener('click', handle, true);
return { destroy() { document.removeEventListener('click', handle, true); } };
}
# Usage: <div use:clickOutside={() => open = false}>...</div>
# ============================================================
# Drill 4 — bind:group for radios
# ============================================================
# A radio group is awkward. Use bind:group.
#
# ANSWER:
# <script>let pick = 'card';</script>
# <label><input type='radio' bind:group={pick} value='card' /> Card</label>
# <label><input type='radio' bind:group={pick} value='bank' /> Bank</label>
# Now 'pick' is always the selected value.
# ============================================================
# Drill 5 — SvelteKit form action with progressive enhancement
# ============================================================
# Build /login that works WITHOUT JS and ALSO upgrades to AJAX with use:enhance.
#
# ANSWER:
# +page.server.ts
# export const actions = {
# default: async ({ request, cookies }) => {
# const data = await request.formData();
# const email = String(data.get('email'));
# // verify, set cookie, throw redirect ...
# return { ok: true };
# }
# };
# +page.svelte
# <script>
# import { enhance } from '$app/forms';
# </script>
# <form method='POST' use:enhance>...</form>
# ============================================================
# Drill 6 — store cleanup in onDestroy
# ============================================================
# A component subscribes to a store imperatively. Where is the leak?
# <script>
# import { count } from './store';
# let n;
# count.subscribe((v) => (n = v));
# </script>
#
# ANSWER: leaks the subscription. Either use the auto-subscription $count,
# or capture the unsubscribe + call it in onDestroy:
# import { onDestroy } from 'svelte';
# const unsub = count.subscribe((v) => (n = v));
# onDestroy(unsub);
# ============================================================
# Scoring
# 6 / 6 -> production-ready Svelte
# 4 / 6 -> revisit svelte/cheatsheet
# < 4 -> the Svelte tutorial walks through these in 30 minutes
Why it matters
Reach for the auto-subscription `\$store` syntax whenever you only need the value; manual `.subscribe()` is the right tool only when you must run side effects inside a non-reactive context, and even then it requires `onDestroy(unsub)` to avoid the leak.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…