ข้ามไปยังเนื้อหา

The $state Rune

$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

เมื่อคุณส่ง 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 ที่เกี่ยวข้อง"]
object แบบ $state คือ reactive proxy

การทำ 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

เพราะ 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>
ใน Svelte 5 คุณ update `$state` ยังไง?
ทำไม `todos.push(newTodo)` ถึง reactive เมื่อ `todos` เป็น `$state([])`?
เมื่อไรควรใช้ `$state.raw`?
`$state.snapshot(user)` คืนอะไร?