Qwik provides multiple state management approaches, each with specific use cases and advantages:
1. useSignal
useSignal is the simplest state management approach in Qwik, suitable for managing primitive values (like numbers, strings, booleans).
Features
- Lightweight, optimal performance
- Can only store a single value
- Access and modify values through
.value - Automatically triggers fine-grained updates
Usage Example
tsximport { component$, useSignal } from '@builder.io/qwik'; export const Counter = component$(() => { const count = useSignal(0); return ( <div> <p>Count: {count.value}</p> <button onClick$={() => count.value++}>Increment</button> <button onClick$={() => count.value--}>Decrement</button> </div> ); });
2. useStore
useStore is used for managing complex object states, capable of storing nested objects and arrays.
Features
- Can store complex objects and arrays
- Supports deep nesting
- Automatically tracks changes to object properties
- Fine-grained updates, only updating changed properties
Usage Example
tsximport { component$, useStore } from '@builder.io/qwik'; export const TodoList = component$(() => { const todos = useStore({ items: [ { id: 1, text: 'Learn Qwik', completed: false }, { id: 2, text: 'Build app', completed: false } ], filter: 'all' }); const addTodo$ = () => { todos.items.push({ id: todos.items.length + 1, text: 'New todo', completed: false }); }; return ( <div> <ul> {todos.items.map(item => ( <li key={item.id}> {item.text} </li> ))} </ul> <button onClick$={addTodo$}>Add Todo</button> </div> ); });
3. useComputed
useComputed is used for creating derived state computed from other states.
Features
- Automatically caches computed results
- Only recomputes when dependencies change
- Suitable for handling complex calculation logic
Usage Example
tsximport { component$, useSignal, useComputed } from '@builder.io/qwik'; export const PriceCalculator = component$(() => { const price = useSignal(100); const tax = useSignal(0.1); const totalPrice = useComputed$(() => { return price.value * (1 + tax.value); }); return ( <div> <p>Price: ${price.value}</p> <p>Tax: {tax.value * 100}%</p> <p>Total: ${totalPrice.value.toFixed(2)}</p> </div> ); });
4. useContext
useContext is used for sharing state across components, similar to React's Context API.
Features
- Avoids passing props through multiple component layers
- Suitable for global state management
- Can be accessed anywhere in the component tree
Usage Example
tsximport { component$, createContext, useContext } from '@builder.io/qwik'; const UserContext = createContext<{ name: string; email: string }>({ name: '', email: '' }); export const UserProvider = component$(() => { const user = { name: 'John Doe', email: 'john@example.com' }; return ( <UserContext.Provider value={user}> <UserProfile /> </UserContext.Provider> ); }); export const UserProfile = component$(() => { const user = useContext(UserContext); return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); });
5. useResource
useResource is used for managing asynchronous data and loading states.
Features
- Handles asynchronous data fetching
- Automatically manages loading states
- Supports error handling
- Can refetch data
Usage Example
tsximport { component$, useResource, useSignal } from '@builder.io/qwik'; import { routeLoader$ } from '@builder.io/qwik-city'; export const useUserData = routeLoader$(async () => { const response = await fetch('https://api.example.com/users'); return response.json(); }); export const UserList = component$(() => { const users = useResource$(({ track }) => { track(() => /* dependencies */); return fetchUsers(); }); return ( <div> {users.value ? ( <ul> {users.value.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ) : ( <p>Loading...</p> )} </div> ); });
6. State Management Best Practices
Choose the Right State Management Approach
- Simple values: Use
useSignal - Complex objects: Use
useStore - Derived state: Use
useComputed - Cross-component sharing: Use
useContext - Asynchronous data: Use
useResource
Avoid Unnecessary Re-renders
- Qwik automatically handles fine-grained updates, no manual optimization needed
- Avoid creating new objects in render functions
- Use
useComputedto cache computed results
State Serialization
- Qwik automatically serializes state to HTML
- Ensure state objects are serializable
- Avoid storing functions or non-serializable objects
Summary: Qwik's state management system is designed to be simple yet powerful, providing flexible state management approaches through different hooks. The compiler automatically handles state serialization and fine-grained updates, allowing developers to focus on business logic without worrying about performance issues.