Astro กับ Nanostores: Shared State ระหว่าง Islands
#astro13 เม.ย. 2569
ปัญหา State ใน Islands Architecture
เมื่อใช้ Islands Architecture แต่ละ island เป็น independent component ทำให้การแชร์ state ระหว่าง islands ทำได้ยาก Nanostores แก้ปัญหานี้ได้
ติดตั้ง
npm install nanostores @nanostores/react
# หรือ
npm install nanostores @nanostores/vue
สร้าง Store
// src/stores/cart.ts
import { atom, computed, map } from 'nanostores';
// Simple atom
export const cartOpen = atom(false);
// Map (object)
export const cartItems = map<Record<string, { name: string; price: number; qty: number }>>({});
// Computed
export const cartTotal = computed(cartItems, items =>
Object.values(items).reduce((sum, item) => sum + item.price * item.qty, 0)
);
export const cartCount = computed(cartItems, items =>
Object.values(items).reduce((sum, item) => sum + item.qty, 0)
);
// Actions
export function addToCart(id: string, name: string, price: number) {
const current = cartItems.get()[id];
if (current) {
cartItems.setKey(id, { ...current, qty: current.qty + 1 });
} else {
cartItems.setKey(id, { name, price, qty: 1 });
}
}
export function removeFromCart(id: string) {
const items = { ...cartItems.get() };
delete items[id];
cartItems.set(items);
}
ใช้ใน React Island
// src/components/CartButton.tsx
import { useStore } from '@nanostores/react';
import { cartCount, cartOpen } from '../stores/cart';
export default function CartButton() {
const count = useStore(cartCount);
const open = useStore(cartOpen);
return (
<button onClick={() => cartOpen.set(!open)}>
🛒 ({count})
</button>
);
}
ใช้ใน Vue Island
<!-- src/components/CartDrawer.vue -->
<script setup>
import { useStore } from '@nanostores/vue';
import { cartItems, cartTotal, removeFromCart } from '../stores/cart';
const items = useStore(cartItems);
const total = useStore(cartTotal);
</script>
<template>
<div v-for="(item, id) in items" :key="id">
{{ item.name }} x{{ item.qty }} = ฿{{ item.price * item.qty }}
<button @click="removeFromCart(id)">ลบ</button>
</div>
<p>รวม: ฿{{ total }}</p>
</template>
ใช้ใน Astro Page
---
import CartButton from '../components/CartButton.tsx';
import CartDrawer from '../components/CartDrawer.vue';
import ProductCard from '../components/ProductCard.tsx';
---
<CartButton client:load />
<CartDrawer client:load />
<ProductCard client:visible />
<!-- ทั้งสาม island แชร์ cart state เดียวกัน -->
สรุป
Nanostores เป็น state management ที่เบามาก (< 1KB) และทำงานได้กับทุก framework ทำให้แชร์ state ระหว่าง React, Vue, Svelte islands ได้อย่างง่ายดายครับ