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

The $derived Rune

$derived ประกาศค่าที่คำนวณจากค่า reactive อื่น จะ recompute อัตโนมัติทุกครั้งที่ค่าที่อ่านเปลี่ยน — และ cache ไว้ ดังนั้นจะ recompute เฉพาะตอน dependency เปลี่ยนจริง ๆ

<script>
let count = $state(2);
let double = $derived(count * 2); // 4, and stays in sync
let label = $derived(`count is ${count}`);
</script>
<p>{double}{label}</p>

คุณไม่ต้อง list dependency เลย compiler เห็นว่า double อ่าน count แล้ว rebuild double ตอน count เปลี่ยนพอดี ค่า derived เป็น read-only — คุณไม่ assign ค่าให้ตรง ๆ เพราะเป็น function ของ input

$derived(expr) รับ expression เดียว เมื่อการคำนวณต้องใช้หลาย statement — loop, ตัวแปรชั่วคราว, branch — ให้ใช้ $derived.by พร้อม function ที่ return ค่า

<script>
let numbers = $state([1, 2, 3, 4]);
let stats = $derived.by(() => {
let sum = 0;
for (const n of numbers) sum += n;
return { sum, avg: sum / numbers.length };
});
</script>
<p>sum {stats.sum}, avg {stats.avg}</p>

$derived(x) ก็แค่ shorthand ของ $derived.by(() => x) dependency ยัง track อัตโนมัติ — อะไรก็ตามที่ function อ่านจะกลายเป็น dependency

flowchart LR
  a["$state count"] --> d["$derived double = count * 2"]
  b["$state numbers"] --> e["$derived.by stats"]
  d --> ui["DOM update เมื่อ input เปลี่ยน"]
  e --> ui
derived value recompute จาก dependency ที่ track

ความผิดพลาดแบบ React-brain ที่พบบ่อยคือคำนวณค่าใน effect แล้วเขียนลง state:

<script>
let count = $state(2);
let double = $state(0);
// ❌ Don't do this — an effect that mirrors derived state.
$effect(() => { double = count * 2; });
</script>

แบบนี้แย่กว่าในทุกทาง: มี state ตัวเกิน, มี update cycle เกิน, มีโอกาสที่ double จะ out of sync ชั่วขณะ และ data flow ตามยากขึ้น เครื่องมือที่ถูกคือ $derived:

<script>
let count = $state(2);
let double = $derived(count * 2); // ✅ one source of truth, always in sync
</script>

กฎง่าย ๆ: ถ้าค่าหนึ่งคำนวณได้จาก state อื่น ให้ derive ค่านั้นออกมา — อย่าเก็บแล้ว sync derived state คือ source of truth เดียว สอดคล้องเสมอ และง่ายกว่า เก็บ $effect ไว้ใช้กับ side effect จริง ๆ (บทหน้า) ไม่ใช่คำนวณค่า

`$derived` ทำอะไร?
เมื่อไรใช้ `$derived.by` แทน `$derived`?
Svelte รู้ได้ยังไงว่าค่า `$derived` ขึ้นกับ `count`?
ทำไมเลือก `$derived` แทน `$effect` ที่เขียน derived state?