State Management
On this page 27
stx provides powerful state management capabilities for building complex applications. This page covers all state management features and patterns available in the stx ecosystem.
Local Component State
Reactive State
@component('')
({
count: 0,
isLoading: false
})
<divclass="">
<button@click="count--"x-disabled=""></button>
<span>{{ count }}</span>
<button@click="count++"x-disabled=""></button>
</div>
Computed Properties
@component('')
({
firstName: 'John',
lastName: 'Doe',
age: 25
})
({
fullName: () => `${firstName} ${lastName}`,
isAdult: () => age >= 18,
displayName: () => isAdult ? fullName : firstName
})
<divclass="">
<h1>{{ displayName }}</h1>
<p>Age: {{ age }} ({{ isAdult ? 'Adult' : 'Minor' }})</p>
</div>
Watchers
@component('')
({
query: '',
results: [],
isSearching: false
})
('query', async (newQuery, oldQuery) => {
if (newQuery !== oldQuery && newQuery.length > 2) {
isSearching = true
results = await searchAPI(newQuery)
isSearching = false
}
})
<divclass="">
<input@model="query"placeholder="">
@if(isSearching)
<spinner />
@foreach(resultsasresult)
<divclass="">{{ result.title }}</div>
</div>
Global State Management
Store Creation
import { createStore } from '@stacksjs/state'
interface AppState {
user: User | null
theme: 'light' | 'dark'
notifications: Notification[]
}
const store = createStore<AppState>({
state: {
user: null,
theme: 'light',
notifications: []
},
getters: {
isAuthenticated: (state) => state.user !== null,
unreadCount: (state) => state.notifications.filter(n => !n.read).length
},
actions: {
async login(email: string, password: string) {
const user = await authAPI.login(email, password)
this.state.user = user
},
toggleTheme() {
this.state.theme = this.state.theme === 'light' ? 'dark' : 'light'
},
addNotification(notification: Notification) {
this.state.notifications.push(notification)
}
}
})
Using Store in Components
@component('')
(store, ['user', 'theme'])
<headerclass=""x-class="">
@if(user)
<divclass="">
<imgx-src=""x-alt="">
<span>{{ user.name }}</span>
</div>
<login-button />
</header>
Store Modules
// modules/auth.ts
export const authModule = {
namespaced: true,
state: {
user: null,
token: null,
permissions: []
},
actions: {
async login({ commit }, credentials) {
const response = await authAPI.login(credentials)
commit('setUser', response.user)
commit('setToken', response.token)
},
logout({ commit }) {
commit('setUser', null)
commit('setToken', null)
localStorage.removeItem('auth_token')
}
},
mutations: {
setUser(state, user) {
state.user = user
},
setToken(state, token) {
state.token = token
if (token) {
localStorage.setItem('auth_token', token)
}
}
}
}
// Store setup
const store = createStore({
modules: {
auth: authModule,
ui: uiModule,
data: dataModule
}
})
State Persistence
LocalStorage Integration
import { createPersistedStore } from '@stacksjs/state'
const store = createPersistedStore({
key: 'app-state',
storage: localStorage,
state: {
preferences: {
theme: 'light',
language: 'en',
timezone: 'UTC'
}
}
})
Custom Persistence
const store = createStore({
state: initialState,
plugins: [
persistence({
key: 'app-state',
paths: ['user', 'preferences'],
// Custom serializer
serialize: (state) => JSON.stringify(state),
deserialize: (data) => JSON.parse(data),
// Custom storage
storage: {
getItem: (key) => database.get(key),
setItem: (key, value) => database.set(key, value),
removeItem: (key) => database.delete(key)
}
})
]
})
Async State Management
Loading States
@component('')
({
users: [],
loading: false,
error: null
})
fetchUsers() {
loading = true
error = null
try {
users = await userAPI.getAll()
} catch (err) {
error = err.message
} finally {
loading = false
}
}
(() => {
fetchUsers()
})
<divclass="">
@if(loading)
<loading-spinner />
@elseif(error)
<error-message:message="error" />
@foreach(usersasuser)
<user-card:user="user" />
</div>
Resource Management
import { createResource } from '@stacksjs/state'
const userResource = createResource({
fetcher: (id: string) => userAPI.get(id),
cache: {
ttl: 300000, // 5 minutes
maxSize: 100
}
})
// In component
const user = userResource.get(userId)
// Returns: { data, loading, error, refetch }
Optimistic Updates
const store = createStore({
state: {
posts: []
},
actions: {
async createPost(post) {
// Optimistic update
const tempId = generateTempId()
const optimisticPost = { ...post, id: tempId, pending: true }
this.state.posts.unshift(optimisticPost)
try {
const createdPost = await postAPI.create(post)
// Replace optimistic post with real one
const index = this.state.posts.findIndex(p => p.id === tempId)
this.state.posts[index] = createdPost
} catch (error) {
// Remove optimistic post on error
this.state.posts = this.state.posts.filter(p => p.id !== tempId)
throw error
}
}
}
})
State Composition
Composables
// composables/useAuth.ts
export function useAuth() {
const { user, isAuthenticated } = useStore(['user'])
const login = async (credentials) => {
await store.dispatch('auth/login', credentials)
}
const logout = () => {
store.dispatch('auth/logout')
}
return {
user: readonly(user),
isAuthenticated: readonly(isAuthenticated),
login,
logout
}
}
// In component
@component('LoginForm')
@setup(() => {
const { user, login, logout } = useAuth()
return { user, login, logout }
})
<!-- Component template -->
@endcomponent
State Providers
('ThemeProvider', {
state: {
theme: 'light',
colors: lightColors
},
actions: {
setTheme(newTheme) {
this.theme = newTheme
this.colors = newTheme === 'light' ? lightColors : darkColors
}
}
})
<divclass=""x-class="">
<slot />
</div>
@component('')
<theme-provider>
<header />
<main />
<footer />
</theme-provider>
State Debugging
DevTools Integration
const store = createStore({
state: initialState,
plugins: [
devtools({
enabled: process.env.NODE_ENV === 'development',
name: 'stx App Store',
trace: true,
logActions: true,
logMutations: true
})
]
})
Time Travel Debugging
// Enable history tracking
const store = createStore({
state: initialState,
plugins: [
history({
maxHistoryLength: 50,
debounce: 100
})
]
})
// Time travel controls
store.history.undo()
store.history.redo()
store.history.jumpTo(10) // Jump to specific point
Performance Optimization
Selective Subscriptions
@component('')
<!-- Only subscribes to user.profile, not entire user object -->
(store, ['user.profile.name', 'user.profile.email'])
<divclass="">
<h1>{{ user.profile.name }}</h1>
<p>{{ user.profile.email }}</p>
</div>
Memoization
const store = createStore({
state: {
users: [],
filters: { status: 'active', role: 'user' }
},
getters: {
// Memoized getter - only recalculates when dependencies change
filteredUsers: memoize((state) => {
return state.users.filter(user =>
user.status === state.filters.status &&
user.role === state.filters.role
)
}, ['users', 'filters'])
}
})
Testing State
State Testing
import { createTestStore } from '@stacksjs/testing'
describe('User Store', () => {
let store
beforeEach(() => {
store = createTestStore({
modules: { auth: authModule }
})
})
test('login action', async () => {
const credentials = { email: 'user@example.com', password: 'password' }
await store.dispatch('auth/login', credentials)
expect(store.state.auth.user).toBeTruthy()
expect(store.getters['auth/isAuthenticated']).toBe(true)
})
test('logout action', () => {
store.commit('auth/setUser', mockUser)
store.dispatch('auth/logout')
expect(store.state.auth.user).toBeNull()
})
})
Related Resources
- State Guide - Comprehensive state management guide
- Component API - Component state management
- Performance Guide - State performance optimization
- Testing Guide - State testing strategies