The $state Rune
ประกาศ reactive state
หัวข้อที่มีชื่อว่า “ประกาศ reactive state”$state สร้างค่า reactive ที่คุณอ่านและ reassign ได้เหมือนตัวแปรปกติ — compiler ทำให้ทุกการอ่านเป็น dependency และทุกการเขียนเป็น trigger
<script> let count = $state(0);</script>
<button onclick={() => count++}>{count}</button>ไม่มี setter ไม่มี update call พิเศษ count++ คือการ reassign จริง ๆ และเพราะ compiler รู้ว่า text ของปุ่มอ่าน count text node นั้นจะ update
Deep reactivity ผ่าน proxy
หัวข้อที่มีชื่อว่า “Deep reactivity ผ่าน proxy”เมื่อคุณส่ง object หรือ array เข้า $state Svelte จะห่อค่านั้นด้วย proxy เพื่อให้ mutation — ไม่ใช่แค่การ reassign — เป็น reactive การ push เข้า array หรือ set nested property จะ trigger update
<script> let todos = $state([{ text: 'learn runes', done: false }]);
function toggle(todo) { todo.done = !todo.done; // mutating a nested property is reactive } function add(text) { todos.push({ text, done: false }); // mutating the array is reactive }</script>นี่ต่างจาก React จริง ๆ ที่คุณต้องสร้าง object/array ใหม่ ใน Svelte แค่ todos.push(...) และ todo.done = true ก็ทำงาน — proxy จับ mutation ได้ reactivity เป็นแบบ deep: nested object และ array ก็เป็น proxy ด้วย
flowchart LR write["mutate: todo.done = true"] --> proxy["$state proxy จับการเปลี่ยน"] proxy --> update["update เฉพาะ DOM ที่เกี่ยวข้อง"]
Opt out: $state.raw
หัวข้อที่มีชื่อว่า “Opt out: $state.raw”การทำ deep proxy มีต้นทุนนิดหน่อยและไม่ได้อยากได้เสมอไป — สำหรับข้อมูลใหญ่ที่ immutable และคุณแค่ replace ทั้งก้อน ให้ใช้ $state.raw raw state เป็น reactive ตอน reassign แต่ไม่ใช่ตอน mutate
<script> let settings = $state.raw({ theme: 'dark', density: 'cozy' });
// ✅ Reassigning a whole new object IS reactive: settings = { ...settings, theme: 'light' };
// ❌ Mutating is NOT tracked with raw state: // settings.theme = 'light'; // no update</script>ใช้ $state.raw กับค่าที่คุณ treat แบบ immutable (replace ทั้งก้อน) หรือ structure ใหญ่ ๆ ที่คุณไม่อยากได้ overhead ของ proxy
เอา plain copy: $state.snapshot
หัวข้อที่มีชื่อว่า “เอา plain copy: $state.snapshot”เพราะ object แบบ $state คือ proxy การส่งต่อเข้า code ที่คาดหวัง object ธรรมดา (console.log ที่โชว์ Proxy, structuredClone, library ภายนอก) อาจเซอร์ไพรส์ $state.snapshot คืน copy ที่เป็น plain และไม่ reactive
<script> let user = $state({ name: 'Ada', roles: ['admin'] });
function save() { const plain = $state.snapshot(user); // a normal object, not a proxy api.save(plain); }</script>