Skip to content

Svelte Stores - Quản lý State trong Svelte

Svelte Stores là một cách đơn giản và mạnh mẽ để quản lý state (trạng thái) trong ứng dụng Svelte. Khác với Redux hay Context API trong React, Svelte Stores được tích hợp sẵn và cực kỳ dễ sử dụng.

src/stores/counter.js
import { writable } from 'svelte/store';
export const count = writable(0);
src/stores/user.js
import { readable } from 'svelte/store';
const initialUser = { name: 'Guest', email: '' };
export const user = readable(initialUser);

Derived Store (tính toán từ store khác)

Section titled “Derived Store (tính toán từ store khác)”
src/stores/cart.js
import { writable, derived } from 'svelte/store';
export const products = writable([
{ id: 1, name: 'Sản phẩm A', price: 100000 },
{ id: 2, name: 'Sản phẩm B', price: 200000 },
]);
// Tự động tính tổng
export const totalPrice = derived(products, ($products) =>
$products.reduce((sum, p) => sum + p.price, 0)
);
<script>
import { count } from '../stores/counter.js';
// Tự động unsubscribe khi component bị hủy
const unsubscribe = count.subscribe(value => {
console.log('Count:', value);
});
</script>
<h1>Count: {$count}</h1>
<button on:click={() => $count++}>Tăng</button>
<script>
import { count } from '../stores/counter.js';
</script>
<!-- $count tự động subscribe/unsubscribe -->
<h1>Count: {$count}</h1>
<button on:click={() => $count++}>
Tăng (+1)
</button>
<button on:click={() => $count--}>
Giảm (-1)
</button>
<button on:click={() => $count = 0}>
Reset
</button>
import { writable } from 'svelte/store';
const count = writable(0);
count.set(10); // Đặt giá trị = 10
const count = writable(0);
count.update(n => n + 1); // Tăng lên 1
// Hoặc với logic phức tạp
count.update(n => {
if (n >= 10) return 0;
return n + 1;
});
const count = writable(0);
const unsubscribe = count.subscribe(value => {
console.log(value);
});
unsubscribe(); // Hủy subscription
src/stores/cart.js
import { writable, derived } from 'svelte/store';
function createCart() {
const { subscribe, set, update } = writable([]);
return {
subscribe,
addItem: (product) => update(items => {
const existing = items.find(i => i.id === product.id);
if (existing) {
return items.map(i =>
i.id === product.id
? { ...i, quantity: i.quantity + 1 }
: i
);
}
return [...items, { ...product, quantity: 1 }];
}),
removeItem: (id) => update(items =>
items.filter(i => i.id !== id)
),
clear: () => set([]),
updateQuantity: (id, quantity) => update(items =>
items.map(i =>
i.id === id ? { ...i, quantity } : i
)
)
};
}
export const cart = createCart();
// Derived: Tổng tiền
export const cartTotal = derived(cart, ($cart) =>
$cart.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
// Derived: Số lượng items
export const cartCount = derived(cart, ($cart) =>
$cart.reduce((sum, item) => sum + item.quantity, 0)
);
src/components/ProductCard.svelte
<script>
import { cart } from '../stores/cart.js';
export let product;
function addToCart() {
cart.addItem(product);
}
</script>
<div class="product-card">
<h3>{product.name}</h3>
<p>{product.price.toLocaleString()}đ</p>
<button on:click={addToCart}>Thêm vào giỏ</button>
</div>
src/components/CartIcon.svelte
<script>
import { cartCount, cartTotal } from '../stores/cart.js';
</script>
<a href="/cart" class="cart-icon">
🛒 Giỏ hàng
{#if $cartCount > 0}
<span class="badge">{$cartCount}</span>
{/if}
<span class="total">{$cartTotal.toLocaleString()}đ</span>
</a>
<style>
.cart-icon {
position: relative;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.badge {
background: red;
color: white;
border-radius: 50%;
padding: 0.2rem 0.5rem;
font-size: 0.75rem;
}
</style>

Khi dùng Svelte trong Astro, bạn cần lưu ý:

---
// ⚠️ Store chỉ hoạt động ở client-side
// Không dùng được cho SSR data fetching
---
<script>
import { count } from '../stores/counter.js';
</script>
<!-- ✅ Hoạt động: Interactive component -->
<Counter client:load />
<!-- ⚠️ Lưu ý: Mỗi island là một instance riêng biệt -->
<!-- State không được chia sẻ giữa các islands -->
src/stores/persistent.js
import { writable } from 'svelte/store';
function createPersistentStore(key, startValue) {
const stored = localStorage.getItem(key);
const initial = stored ? JSON.parse(stored) : startValue;
const { subscribe, set, update } = writable(initial);
return {
subscribe,
set: (value) => {
localStorage.setItem(key, JSON.stringify(value));
set(value);
},
update: (fn) => {
update(value => {
const newValue = fn(value);
localStorage.setItem(key, JSON.stringify(newValue));
return newValue;
});
}
};
}
export const theme = createPersistentStore('theme', 'light');
export const userPrefs = createPersistentStore('prefs', {});
  1. Tạo file store riêng - Đặt trong src/stores/

  2. Dùng $ prefix - Svelte tự động subscribe/unsubscribe

  3. Tránh global state không cần thiết - Chỉ dùng store khi cần chia sẻ state

  4. Dùng derived stores - Tính toán từ store khác thay vì trong component

  5. Persistent stores - Với localStorage cho settings, theme

Svelte Stores là một trong những điểm mạnh nhất của Svelte:

  • Đơn giản - Không boilerplate
  • Hiệu năng cao - Tự động subscription management
  • TypeScript friendly - Dễ dàng typing
  • Devtools hỗ trợ - Svelte DevTools hiển thị stores

Hãy sử dụng stores cho state cần chia sẻ giữa nhiều components!