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

Cross-Component State

cross-component state ที่ง่ายที่สุดคือค่า reactive ที่ export จาก .svelte.js module ทุกคนที่ import จะ share instance เดียว:

theme.svelte.js
export const theme = $state({ mode: 'light' });
export function toggle() {
theme.mode = theme.mode === 'light' ? 'dark' : 'light';
}

คอมโพเนนต์ใด ๆ import theme และ toggle แล้วจะ sync กัน เหมาะกับ state ที่ global จริง ๆ และมี instance เดียว: color theme, toast queue, feature-flag set

มีข้อควรระวังที่สำคัญทันทีที่คุณใช้ SvelteKit (หรือ SSR ใด ๆ) module-level $state คือ instance เดียวที่ทุกคนที่ import แชร์กัน — รวมถึงบน server ที่ ทุก request ที่ถูกจัดการโดย process เดียวกัน เอา logged-in user ไปไว้ใน module-level singleton แล้วภายใต้ SSR request หนึ่งอาจเห็นข้อมูลของ user อีกคนได้

เมื่อคุณต้องการ state ที่ share ภายใน subtree แต่ แยกกันต่อ instance — และปลอดภัยภายใต้ SSR — ให้ผสม runed class/object กับ setContext / getContext parent สร้าง state แล้วเอาไปไว้ใน context ส่วน descendant อ่านค่าไปใช้ แต่ละ instance ของคอมโพเนนต์ได้ของตัวเอง สร้างตอน render ของ request นั้น

Parent.svelte
<script>
import { setContext } from 'svelte';
// created fresh per instance / per request — not a module singleton
const cart = $state({ items: [] });
setContext('cart', cart);
</script>
<slot />
DeepChild.svelte
<script>
import { getContext } from 'svelte';
const cart = getContext('cart'); // the same reactive object the parent set
</script>
<button onclick={() => cart.items.push(item)}>add ({cart.items.length})</button>

เพราะ state ถูกสร้างภายใน component tree (ไม่ใช่ตอน module load) แต่ละ render — และแต่ละ SSR request — ได้ copy ของตัวเอง idiom ที่พบบ่อยคือ factory เล็ก ๆ (createCart()) ที่ return runed state เรียกใน parent แล้ว share ผ่าน context

flowchart TB
  local["แค่คอมโพเนนต์เดียว: local state"] --> up["ไม่กี่คอมโพเนนต์ที่เกี่ยวกัน: lift state ไป parent แล้วส่ง props"]
  up --> ctx["subtree แบบ per-instance หรือ per-user: setContext + runed state"]
  ctx --> mod["global จริง ๆ instance เดียว: export state จาก .svelte.js module"]
  mod --> store["ต้องการ subscribe contract: ใช้ store"]
แต่ละแพทเทิร์นเหมาะกับที่ไหน
  1. Local$state ในคอมโพเนนต์ เริ่มที่นี่
  2. Lift — ย้าย $state ไปที่ parent ร่วมที่ใกล้ที่สุด ส่งลงเป็น props สำหรับคอมโพเนนต์ที่เกี่ยวกันไม่กี่ตัว
  3. ContextsetContext/getContext กับ runed state สำหรับ subtree ที่ต้องการ instance ของตัวเอง (และความปลอดภัยแบบ SSR)
  4. Module — export $state จาก .svelte.js module สำหรับ state ที่ global จริง ๆ instance เดียว
  5. Store — เมื่อต้องการ subscribe contract (interop, external push source)

ให้เลือกระดับ ต่ำที่สุด ที่แก้ปัญหาได้ แล้วค่อยเลื่อนขึ้นเมื่อจำเป็นเท่านั้น

กับดัก SSR singleton คืออะไร?
คุณ share state ภายใน subtree อย่างปลอดภัยแบบ per-instance / per-request ยังไง?
`.svelte.js` module singleton เหมาะกับ state แบบไหน?
อะไรคือหลักในการเลือกแพทเทิร์น sharing?