Lifecycle — onMount, onDestroy, tick
React uses a single useEffect for everything: mount, update, and cleanup. Svelte 5 separates concerns more explicitly. $effect handles reactive updates (covered in the previous lesson). onMount and onDestroy handle one-time setup and teardown. tick is a utility for flushing pending DOM updates before reading layout.
The complete React hooks ↔ Svelte 5 mapping table
Section titled “The complete React hooks ↔ Svelte 5 mapping table”| React | Svelte 5 | Notes |
|---|---|---|
useState(init) | $state(init) | Mutate directly, no setter |
useMemo(() => expr, deps) | $derived(expr) | No deps array — compiler-tracked |
useMemo(() => fn(), deps) | $derived.by(() => fn()) | Multi-statement derived |
useEffect(fn, deps) | $effect(fn) | No deps array — auto-tracked |
useEffect(fn, []) | onMount(fn) | Run once after mount |
useEffect(() => cleanup, []) | onDestroy(fn) | Run once on destroy |
useRef(init) | let variable | Plain let for mutable non-reactive ref |
useRef(null) + ref={el} | bind:this={el} | DOM element reference |
useContext(Ctx) | getContext(key) | Read from closest ancestor provider |
createContext() + Provider | setContext(key, value) | Provide context |
| Component props | $props() | Destructure with defaults |
| Callback props (lifting state) | $bindable() + bind: | Two-way without explicit callback |
onMount — run once after mount
Section titled “onMount — run once after mount”function Clock() { const [time, setTime] = React.useState(new Date());
React.useEffect(() => { const id = setInterval(() => setTime(new Date()), 1000); return () => clearInterval(id); }, []);
return <p>{time.toLocaleTimeString()}</p>;}<script> import { onMount } from 'svelte'; let time = $state(new Date());
onMount(() => { const id = setInterval(() => { time = new Date(); }, 1000); return () => clearInterval(id); });</script>
<p>{time.toLocaleTimeString()}</p>onMount accepts a setup function and optionally returns a cleanup function, which runs on destroy — identical semantics to useEffect(fn, []).
onDestroy — run once on destroy
Section titled “onDestroy — run once on destroy”function Analytics() { React.useEffect(() => { trackPageView('/dashboard'); return () => { trackPageLeave('/dashboard'); }; }, []); return <div>Dashboard</div>;}<script> import { onMount, onDestroy } from 'svelte';
onMount(() => trackPageView('/dashboard')); onDestroy(() => trackPageLeave('/dashboard'));</script>
<div>Dashboard</div>Splitting mount and destroy into two calls is more readable when the setup and teardown are unrelated.
tick — await DOM flush
Section titled “tick — await DOM flush”tick() returns a promise that resolves after Svelte has finished updating the DOM. Use it when you need to read layout (e.g. element dimensions, focus) immediately after a reactive change — equivalent to await flushSync + requestAnimationFrame in React.
function AutoScroll() { const ref = React.useRef(null); const [items, setItems] = React.useState(['a', 'b', 'c']);
function addItem() { setItems(prev => [...prev, String.fromCharCode(97 + prev.length)]); // Must use useEffect to scroll after render }
React.useEffect(() => { ref.current?.scrollIntoView({ block: 'end' }); }, [items]);
return ( <ul> {items.map(i => <li key={i}>{i}</li>)} <li ref={ref} /> </ul> );}<script> import { tick } from 'svelte'; let items = $state(['a', 'b', 'c']); let endEl;
async function addItem() { items.push(String.fromCharCode(97 + items.length)); await tick(); endEl?.scrollIntoView({ block: 'end' }); }</script>
<ul> {#each items as item} <li>{item}</li> {/each} <li bind:this={endEl}></li></ul><button onclick={addItem}>Add</button>