Async Operations
Handle asynchronous data fetching and updates with built-in loading states.
Async Data Fetching (Getters)
Getters allow you to fetch data asynchronously with automatic loading states:
const [useStorken] = create({
getters: {
// Simple fetch
user: async () => {
const response = await fetch('/api/user')
return response.json()
},
// With parameters
posts: async (storken, userId: string, page = 1) => {
const response = await fetch(
`/api/users/${userId}/posts?page=${page}`
)
return response.json()
},
// With error handling
profile: async (storken, id: string) => {
try {
const response = await fetch(`/api/profiles/${id}`)
if (!response.ok) {
throw new Error('Profile not found')
}
return response.json()
} catch (error) {
console.error('Failed to fetch profile:', error)
return null
}
}
}
})
Using Getters in Components
function UserProfile() {
const [user, , , loading, refetch] = useStorken('user')
useEffect(() => {
// Initial fetch
refetch()
}, [refetch])
if (loading) {
return <Spinner />
}
if (!user) {
return <div>No user found</div>
}
return (
<div>
<h1>{user.name}</h1>
<button onClick={() => refetch()}>
Refresh
</button>
</div>
)
}
Async Updates (Setters)
Setters allow you to perform async operations when state changes:
const [useStorken] = create({
setters: {
// Save to backend
profile: async (storken, profile: Profile) => {
const response = await fetch('/api/profile', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(profile)
})
if (!response.ok) {
throw new Error('Failed to save profile')
}
// Update related states
const updated = await response.json()
storken.sky.set('lastUpdated', new Date())
return updated
},
// Delete with confirmation
todo: async (storken, todo: Todo | null, action?: string) => {
if (action === 'delete' && todo) {
await fetch(`/api/todos/${todo.id}`, {
method: 'DELETE'
})
// Remove from list
const todos = storken.sky.get('todos') || []
storken.sky.set('todos',
todos.filter(t => t.id !== todo.id)
)
}
}
}
})
Using Setters in Components
function ProfileEditor() {
const [profile, setProfile, , saving] = useStorken('profile')
const handleSave = async (updates: Partial<Profile>) => {
try {
await setProfile({ ...profile, ...updates })
toast.success('Profile saved!')
} catch (error) {
toast.error('Failed to save profile')
}
}
return (
<form onSubmit={e => {
e.preventDefault()
handleSave(formData)
}}>
{/* Form fields */}
<button type="submit" disabled={saving}>
{saving ? 'Saving...' : 'Save'}
</button>
</form>
)
}
Loading States
Storken automatically manages loading states for async operations:
function DataComponent() {
const [
data, // The current value
setData, // Setter function
reset, // Reset function
loading, // Loading state (true during async ops)
refetch // Trigger getter again
] = useStorken('data')
// Loading states for different scenarios
if (loading && !data) {
// Initial load
return <FullPageLoader />
}
if (loading && data) {
// Refreshing existing data
return (
<div className="relative">
<div className="opacity-50">{/* Show existing data */}</div>
<LoadingOverlay />
</div>
)
}
if (!loading && !data) {
// No data available
return <EmptyState onRetry={refetch} />
}
// Data loaded successfully
return <DataDisplay data={data} onRefresh={refetch} />
}
Error Handling
Implement robust error handling for async operations:
// Store configuration with error handling
const [useStorken] = create({
getters: {
data: async (storken, id: string) => {
try {
const response = await fetch(`/api/data/${id}`)
if (!response.ok) {
// Store error state
storken.sky.set('dataError', {
status: response.status,
message: `Failed to fetch data: ${response.statusText}`
})
return null
}
// Clear any previous errors
storken.sky.set('dataError', null)
return response.json()
} catch (error) {
// Network or other errors
storken.sky.set('dataError', {
message: error.message || 'An unexpected error occurred'
})
return null
}
}
}
})
// Component with error handling
function DataWithError() {
const [data, , , loading, refetch] = useStorken('data')
const [error] = useStorken('dataError')
useEffect(() => {
refetch('123')
}, [refetch])
if (loading) return <Loading />
if (error) {
return (
<ErrorDisplay
message={error.message}
onRetry={() => refetch('123')}
/>
)
}
if (!data) return <NoData />
return <DataDisplay data={data} />
}
Polling & Real-time Updates
Implement polling or real-time updates with Storken:
// Polling implementation
function usePolling(key: string, interval = 5000) {
const [data, , , loading, refetch] = useStorken(key)
useEffect(() => {
// Initial fetch
refetch()
// Set up polling
const timer = setInterval(() => {
refetch()
}, interval)
return () => clearInterval(timer)
}, [refetch, interval])
return { data, loading, refetch }
}
// WebSocket integration
function useRealtimeData(key: string) {
const [data, setData] = useStorken(key)
useEffect(() => {
const ws = new WebSocket('wss://api.example.com/stream')
ws.onmessage = (event) => {
const update = JSON.parse(event.data)
// Update state with real-time data
setData(prev => ({
...prev,
...update
}))
}
ws.onerror = (error) => {
console.error('WebSocket error:', error)
}
return () => ws.close()
}, [setData])
return data
}
Caching & Deduplication
Implement caching to avoid unnecessary requests:
// Cache plugin
const cachePlugin = (ttl = 60000) => (storken) => {
const cache = new Map()
const timestamps = new Map()
// Intercept getter calls
const originalGetter = storken.opts?.getter
if (originalGetter) {
storken.opts.getter = async (...args) => {
const cacheKey = JSON.stringify(args)
const cached = cache.get(cacheKey)
const timestamp = timestamps.get(cacheKey)
// Return cached if still valid
if (cached && timestamp && Date.now() - timestamp < ttl) {
return cached
}
// Fetch fresh data
const fresh = await originalGetter.call(storken, ...args)
cache.set(cacheKey, fresh)
timestamps.set(cacheKey, Date.now())
return fresh
}
}
return {
clear: () => {
cache.clear()
timestamps.clear()
},
invalidate: (key?: string) => {
if (key) {
cache.delete(key)
timestamps.delete(key)
} else {
cache.clear()
timestamps.clear()
}
}
}
}
// Use with caching
const [useStorken] = create({
getters: {
user: async (storken, id) => {
// This will be cached
const response = await fetch(`/api/users/${id}`)
return response.json()
}
},
plugins: {
cache: cachePlugin(30000) // 30 second TTL
}
})
🚀 Performance Tips
- • Use caching for frequently accessed data
- • Implement request deduplication
- • Show stale data while refreshing
- • Use optimistic updates for better UX
- • Handle errors gracefully
- • Consider pagination for large datasets