Lifecycle — onMount, onDestroy, tick
React ใช้ useEffect เดียวสำหรับทุกอย่าง: mount, update และ cleanup Svelte 5 แยกความรับผิดชอบชัดเจนกว่า $effect จัดการ reactive updates (ครอบคลุมในบทเรียนก่อนหน้า) onMount และ onDestroy จัดการ setup และ teardown ที่รันครั้งเดียว tick เป็น utility สำหรับ flush pending DOM updates ก่อนอ่าน layout
ตารางเปรียบเทียบ React hooks ↔ Svelte 5 แบบครบสมบูรณ์
หัวข้อที่มีชื่อว่า “ตารางเปรียบเทียบ React hooks ↔ Svelte 5 แบบครบสมบูรณ์”| React | Svelte 5 | หมายเหตุ |
|---|---|---|
useState(init) | $state(init) | Mutate ตรงได้เลย ไม่มี setter |
useMemo(() => expr, deps) | $derived(expr) | ไม่มี deps array — compiler track |
useMemo(() => fn(), deps) | $derived.by(() => fn()) | Multi-statement derived |
useEffect(fn, deps) | $effect(fn) | ไม่มี deps array — auto-tracked |
useEffect(fn, []) | onMount(fn) | รันครั้งเดียวหลัง mount |
useEffect(() => cleanup, []) | onDestroy(fn) | รันครั้งเดียวเมื่อ destroy |
useRef(init) | let variable | let ธรรมดาสำหรับ mutable non-reactive ref |
useRef(null) + ref={el} | bind:this={el} | DOM element reference |
useContext(Ctx) | getContext(key) | อ่านจาก ancestor provider ที่ใกล้ที่สุด |
createContext() + Provider | setContext(key, value) | Provide context |
| Component props | $props() | Destructure พร้อม defaults |
| Callback props (lifting state) | $bindable() + bind: | Two-way โดยไม่มี explicit callback |
onMount — รันครั้งเดียวหลัง mount
หัวข้อที่มีชื่อว่า “onMount — รันครั้งเดียวหลัง 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 รับ setup function และ optional return cleanup function ซึ่งรันเมื่อ destroy — semantics เดียวกับ useEffect(fn, []) ทุกอย่าง
onDestroy — รันครั้งเดียวเมื่อ destroy
หัวข้อที่มีชื่อว่า “onDestroy — รันครั้งเดียวเมื่อ 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>การแยก mount และ destroy เป็นสอง call อ่านง่ายกว่าเมื่อ setup และ teardown ไม่เกี่ยวข้องกัน
tick — await DOM flush
หัวข้อที่มีชื่อว่า “tick — await DOM flush”tick() คืน promise ที่ resolve หลัง Svelte อัปเดต DOM เสร็จ ใช้เมื่อต้องอ่าน layout (เช่น ขนาด element, focus) ทันทีหลัง reactive change — เทียบเท่ากับ await flushSync + requestAnimationFrame ใน 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>