乐闻世界logo
搜索文章和话题

How is state management implemented in Astro? How do you manage state in React, Vue, and Svelte components?

2月21日 15:22

State management in Astro is important for building interactive applications. While Astro is static by default, there are multiple ways to implement state management.

Client-Side State Management:

  1. React Integration:

    jsx
    // src/components/Counter.jsx import { useState, useEffect } from 'react'; export function Counter() { const [count, setCount] = useState(0); useEffect(() => { const saved = localStorage.getItem('count'); if (saved) setCount(parseInt(saved)); }, []); useEffect(() => { localStorage.setItem('count', count.toString()); }, [count]); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(c => c + 1)}>Increment</button> <button onClick={() => setCount(c => c - 1)}>Decrement</button> </div> ); }
  2. Vue Integration:

    vue
    <!-- src/components/TodoList.vue --> <script setup> import { ref, computed } from 'vue'; const todos = ref([ { id: 1, text: 'Learn Astro', completed: false }, { id: 2, text: 'Build app', completed: true }, ]); const completedCount = computed(() => todos.value.filter(t => t.completed).length ); function addTodo(text) { todos.value.push({ id: Date.now(), text, completed: false, }); } function toggleTodo(id) { const todo = todos.value.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } </script> <template> <div> <p>Completed: {{ completedCount }} / {{ todos.length }}</p> <ul> <li v-for="todo in todos" :key="todo.id"> <input type="checkbox" :checked="todo.completed" @change="toggleTodo(todo.id)" /> <span :style="{ textDecoration: todo.completed ? 'line-through' : 'none' }"> {{ todo.text }} </span> </li> </ul> </div> </template>
  3. Svelte Integration:

    svelte
    <!-- src/components/ShoppingCart.svelte --> <script> import { writable } from 'svelte/store'; const cart = writable([]); const total = writable(0); function addToCart(product) { cart.update(items => [...items, product]); total.update(t => t + product.price); } function removeFromCart(index) { cart.update(items => { const removed = items[index]; total.update(t => t - removed.price); return items.filter((_, i) => i !== index); }); } </script> <div> <h2>Shopping Cart</h2> <p>Total: ${$total}</p> <ul> {#each $cart as item, index} <li> {item.name} - ${item.price} <button on:click={() => removeFromCart(index)}>Remove</button> </li> {/each} </ul> </div>

Global State Management (Zustand):

typescript
// src/store/useStore.ts import { create } from 'zustand'; interface StoreState { user: User | null; theme: 'light' | 'dark'; setUser: (user: User) => void; setTheme: (theme: 'light' | 'dark') => void; } export const useStore = create<StoreState>((set) => ({ user: null, theme: 'light', setUser: (user) => set({ user }), setTheme: (theme) => set({ theme }), }));
jsx
// src/components/Header.jsx import { useStore } from '../store/useStore'; export function Header() { const { user, theme, setTheme } = useStore(); return ( <header> <h1>My App</h1> {user ? <p>Welcome, {user.name}</p> : <p>Please login</p>} <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> {theme === 'light' ? '🌙' : '☀️'} </button> </header> ); }

Server-Side State Management (React Query):

typescript
// src/lib/api.ts export async function fetchPosts() { const response = await fetch('/api/posts'); return response.json(); } export async function fetchPost(id: string) { const response = await fetch(`/api/posts/${id}`); return response.json(); }
jsx
// src/components/PostList.jsx import { useQuery } from '@tanstack/react-query'; import { fetchPosts } from '../lib/api'; export function PostList() { const { data, isLoading, error } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts, }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <ul> {data.map(post => ( <li key={post.id}> <a href={`/blog/${post.slug}`}>{post.title}</a> </li> ))} </ul> ); }

Form State Management:

jsx
// src/components/ContactForm.jsx import { useState } from 'react'; export function ContactForm() { const [formData, setFormData] = useState({ name: '', email: '', message: '', }); const [errors, setErrors] = useState({}); const [isSubmitting, setIsSubmitting] = useState(false); function handleChange(e) { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); } async function handleSubmit(e) { e.preventDefault(); setIsSubmitting(true); try { const response = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData), }); if (!response.ok) throw new Error('Submission failed'); alert('Message sent!'); setFormData({ name: '', email: '', message: '' }); } catch (error) { setErrors({ submit: error.message }); } finally { setIsSubmitting(false); } } return ( <form onSubmit={handleSubmit}> <input name="name" value={formData.name} onChange={handleChange} placeholder="Name" required /> <input name="email" type="email" value={formData.email} onChange={handleChange} placeholder="Email" required /> <textarea name="message" value={formData.message} onChange={handleChange} placeholder="Message" required /> <button type="submit" disabled={isSubmitting}> {isSubmitting ? 'Sending...' : 'Send'} </button> {errors.submit && <p className="error">{errors.submit}</p>} </form> ); }

URL State Management:

jsx
// src/components/ProductFilter.jsx import { useSearchParams } from 'react-router-dom'; export function ProductFilter() { const [searchParams, setSearchParams] = useSearchParams(); const category = searchParams.get('category') || 'all'; const sort = searchParams.get('sort') || 'name'; const page = parseInt(searchParams.get('page') || '1'); function updateFilter(key, value) { setSearchParams(prev => { const newParams = new URLSearchParams(prev); newParams.set(key, value); return newParams; }); } return ( <div> <select value={category} onChange={(e) => updateFilter('category', e.target.value)} > <option value="all">All Categories</option> <option value="electronics">Electronics</option> <option value="clothing">Clothing</option> </select> <select value={sort} onChange={(e) => updateFilter('sort', e.target.value)} > <option value="name">Name</option> <option value="price">Price</option> <option value="date">Date</option> </select> <div> <button disabled={page === 1} onClick={() => updateFilter('page', page - 1)} > Previous </button> <span>Page {page}</span> <button onClick={() => updateFilter('page', page + 1)}> Next </button> </div> </div> ); }

Server-Side State (SSR):

astro
--- // src/pages/dashboard.astro import { getEntry } from 'astro:content'; // Fetch data on server side const user = await getUser(Astro.request); const posts = await getCollection('blog'); const stats = await fetchStats(user.id); --- <div class="dashboard"> <h1>Welcome, {user.name}</h1> <div class="stats"> <div class="stat"> <h3>Total Posts</h3> <p>{stats.totalPosts}</p> </div> <div class="stat"> <h3>Total Views</h3> <p>{stats.totalViews}</p> </div> </div> <div class="posts"> {posts.map(post => ( <article> <h2>{post.data.title}</h2> <p>{post.data.description}</p> </article> ))} </div> </div>

Best Practices:

  1. Choose Appropriate State Management:

    • Simple state: Use framework built-in state (useState, ref)
    • Complex state: Use state management libraries (Zustand, Redux)
    • Server state: Use data fetching libraries (React Query)
  2. Keep State Simple:

    • Store only necessary data
    • Avoid redundant state
    • Use derived state
  3. Performance Optimization:

    • Use memo to avoid unnecessary re-renders
    • Implement virtual scrolling for large datasets
    • Use debounce and throttle for frequent updates
  4. Type Safety:

    • Use TypeScript to define state types
    • Ensure state updates are type-correct
    • Use type inference to reduce errors
  5. Test State Management:

    • Write unit tests for state logic
    • Test state update functions
    • Mock async state updates

Astro's state management solutions are flexible and diverse, allowing you to choose the most suitable approach based on project requirements.

标签:Astro