| name | state-management-architect |
| description | Design and implement state management solutions using Context API, XState, Zustand, Jotai, and custom hooks with testing patterns and performance optimization |
| allowed-tools | ["Read","Write","Edit","Bash","Glob","Grep","Task"] |
State Management Architect
Expert skill for designing and implementing state management solutions for UI libraries and applications. Specializes in React Context, XState state machines, Zustand, Jotai, custom hooks, and state testing patterns.
Core Capabilities
1. State Management Solutions
- React Context: Provider patterns, context composition
- XState: Finite state machines, statecharts
- Zustand: Simple global state management
- Jotai: Atomic state management
- Custom Hooks: Encapsulated state logic
- Reducers: Complex state transitions
- Middleware: State change interceptors
2. Context Patterns
- Single Context: Simple state sharing
- Multiple Contexts: Domain separation
- Context Composition: Nested providers
- Context Selectors: Optimized subscriptions
- Context with Reducer: Complex state logic
- Context Factories: Reusable context patterns
3. State Machines (XState)
- Finite States: Explicit state definitions
- Transitions: State change logic
- Guards: Conditional transitions
- Actions: Side effects on transitions
- Services: Async operations
- Actors: Spawned state machines
- Visualization: State machine diagrams
4. Global State (Zustand)
- Store Creation: Simple store setup
- Selectors: Optimized subscriptions
- Actions: State mutations
- Middleware: Persist, devtools, immer
- Slices: Modular store organization
- Computed Values: Derived state
5. Atomic State (Jotai)
- Atoms: Primitive state units
- Derived Atoms: Computed state
- Async Atoms: Async data fetching
- Atom Families: Dynamic atoms
- Atom Utils: Reset, update, scope
- Storage: Persistence
6. Performance Optimization
- Memoization: Prevent unnecessary renders
- Selectors: Granular subscriptions
- Code Splitting: Lazy load state
- Batching: Group state updates
- Immutability: Structural sharing
- Devtools: Performance profiling
Workflow
Phase 1: State Analysis
-
Identify State Types
- Local component state?
- Shared state?
- Global state?
- Server state?
-
Map Data Flow
- Who creates state?
- Who reads state?
- Who updates state?
- State lifetime?
-
Choose Solution
- Simple: useState, useReducer
- Shared: Context, props
- Global: Zustand, Jotai
- Complex logic: XState
Phase 2: Implementation
-
Set Up State Management
- Install dependencies
- Create stores/contexts
- Define state shape
-
Implement Logic
- State updates
- Side effects
- Error handling
- Loading states
-
Optimize Performance
- Add selectors
- Memoize components
- Split code
Phase 3: Testing & Documentation
-
Write Tests
- State transitions
- Side effects
- Edge cases
- Performance
-
Document API
- State shape
- Actions/mutations
- Usage examples
- Migration guide
State Management Patterns
Context with Reducer Pattern
import { createContext, useContext, useReducer, ReactNode } from 'react'
interface CounterState {
count: number
loading: boolean
error: string | null
}
type CounterAction =
| { type: 'INCREMENT' }
| { type: 'DECREMENT' }
| { type: 'SET_COUNT'; payload: number }
| { type: 'SET_LOADING'; payload: boolean }
| { type: 'SET_ERROR'; payload: string | null }
function counterReducer(state: CounterState, action: CounterAction): CounterState {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 }
case :
{ ...state, : state. - }
:
{ ...state, : action. }
:
{ ...state, : action. }
:
{ ...state, : action. }
:
state
}
}
{
:
: .<>
}
= createContext< | >()
() {
[state, dispatch] = (counterReducer, {
: ,
: ,
: ,
})
(
)
}
() {
context = ()
(!context) {
()
}
context
}
counterActions = {
: (): ({ : }),
: (): ({ : }),
: (: ): ({ : , : count }),
: (: ): ({ : , : loading }),
: (: | ): ({ : , : error }),
}
() {
{ state, dispatch } = ()
(
)
}
XState State Machine
import { createMachine, assign } from 'xstate'
import { useMachine } from '@xstate/react'
interface ToggleContext {
count: number
}
type ToggleEvent =
| { type: 'TOGGLE' }
| { type: 'RESET' }
export const toggleMachine = createMachine<ToggleContext, ToggleEvent>({
id: 'toggle',
initial: 'off',
context: {
count: 0,
},
states: {
off: {
on: {
TOGGLE: {
target: 'on',
actions: assign({
count: (ctx) => ctx.count + 1,
}),
},
},
},
on: {
on: {
TOGGLE: {
target: 'off',
},
},
},
},
on: {
RESET: {
target: 'off',
actions: assign({
: ,
}),
},
},
})
() {
[state, send] = (toggleMachine)
(
)
}
XState with Async Operations
import { createMachine, assign } from 'xstate'
interface AuthContext {
user: { id: string; name: string } | null
error: string | null
}
type AuthEvent =
| { type: 'LOGIN'; credentials: { email: string; password: string } }
| { type: 'LOGOUT' }
export const authMachine = createMachine<AuthContext, AuthEvent>({
id: 'auth',
initial: 'idle',
context: {
user: null,
error: null,
},
states: {
idle: {
on: {
LOGIN: 'authenticating',
},
},
authenticating: {
invoke: {
src: (context, event) => loginUser(event.credentials),
onDone: {
target: ,
: ({
: event.,
: ,
}),
},
: {
: ,
: ({
: event..,
}),
},
},
},
: {
: {
: {
: ,
: ({
: ,
: ,
}),
},
},
},
},
})
() {
response = (, {
: ,
: .(credentials),
})
(!response.) ()
response.()
}
Zustand Store
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
interface Todo {
id: string
text: string
completed: boolean
}
interface TodoStore {
todos: Todo[]
filter: 'all' | 'active' | 'completed'
addTodo: (text: string) => void
toggleTodo: (id: string) => void
removeTodo: (id: string) => void
setFilter: (filter: 'all' | 'active' | 'completed') => void
filteredTodos: () => Todo[]
}
useTodoStore = create<>()(
(
(
( ({
: [],
: ,
:
( {
state..({
: .().(),
text,
: ,
})
}),
:
( {
todo = state..( t. === id)
(todo) {
todo. = !todo.
}
}),
:
( {
state. = state..( t. !== id)
}),
:
({ filter }),
: {
{ todos, filter } = ()
(filter === ) todos
(filter === ) todos.( !t.)
todos.( t.)
},
})),
{
: ,
}
)
)
)
() {
filteredTodos = ( state.())
toggleTodo = ( state.)
(
)
}
Zustand with Slices
import { create } from 'zustand'
interface UserSlice {
user: { id: string; name: string } | null
setUser: (user: { id: string; name: string }) => void
clearUser: () => void
}
const createUserSlice = (set: any): UserSlice => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
})
interface SettingsSlice {
theme: 'light' | 'dark'
language: string
setTheme: (theme: 'light' | 'dark') => void
setLanguage: () =>
}
createSettingsSlice = (: ): ({
: ,
: ,
: ({ theme }),
: ({ language }),
})
= &
useStore = create<>()( ({
...(set),
...(set),
}))
Jotai Atoms
import { atom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'
export const countAtom = atom(0)
export const doubleCountAtom = atom((get) => get(countAtom) * 2)
export const incrementAtom = atom(
(get) => get(countAtom),
(get, set) => set(countAtom, get(countAtom) + 1)
)
export const userAtom = atom(async () => {
const response = await fetch('/api/user')
return response.json()
})
export const themeAtom = atomWithStorage<'light' | 'dark'>('theme', 'light')
{ atomFamily }
todoAtomFamily = (
({
id,
: ,
: ,
})
)
{ useAtom, useAtomValue, useSetAtom }
() {
[count, setCount] = (countAtom)
doubleCount = (doubleCountAtom)
increment = (incrementAtom)
(
)
}
Custom Hook Pattern
import { useState, useEffect } from 'react'
export function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch (error) {
console.error(error)
return initialValue
}
})
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(storedValue))
} catch (error) {
console.error(error)
}
}, [key, storedValue])
return [storedValue, setStoredValue] as const
}
import { useState, useEffect } from 'react'
interface UseAsyncState<T> {
data: T | null
:
: |
}
useAsync<T>(: <T>, : [] = []) {
[state, setState] = useState<<T>>({
: ,
: ,
: ,
})
( {
cancelled =
({ : , : , : })
()
.( {
(!cancelled) {
({ data, : , : })
}
})
.( {
(!cancelled) {
({ : , : , error })
}
})
{
cancelled =
}
}, dependencies)
state
}
{ useState, useEffect }
useDebounce<T>(: T, : = ): T {
[debouncedValue, setDebouncedValue] = useState<T>(value)
( {
handler = ( {
(value)
}, delay)
{
(handler)
}
}, [value, delay])
debouncedValue
}
State Testing Patterns
Testing Context
import { render, screen } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { CounterProvider, useCounter, counterActions } from './CounterContext'
function TestComponent() {
const { state, dispatch } = useCounter()
return (
<div>
<span data-testid="count">{state.count}</span>
<button onClick={() => dispatch(counterActions.increment())}>+</button>
<button onClick={() => dispatch(counterActions.decrement())}>-</button>
</div>
)
}
describe('CounterContext', () => {
it('increments count', async () => {
const user = userEvent.setup()
render(
<CounterProvider>
<TestComponent />
)
(screen.()).()
user.(screen.(, { : }))
(screen.()).()
})
(, () => {
user = userEvent.()
(
)
user.(screen.(, { : }))
(screen.()).()
})
})
Testing XState
import { interpret } from 'xstate'
import { toggleMachine } from './toggleMachine'
describe('toggleMachine', () => {
it('toggles between on and off', () => {
const service = interpret(toggleMachine).start()
expect(service.state.value).toBe('off')
expect(service.state.context.count).toBe(0)
service.send('TOGGLE')
expect(service.state.value).toBe('on')
expect(service.state.context.count).toBe(1)
service.send('TOGGLE')
expect(service.state.value).toBe('off')
expect(service.state.context.count).()
service.()
})
(, {
service = (toggleMachine).()
service.()
service.()
service.()
(service..).()
(service...).()
service.()
})
})
Testing Zustand
import { renderHook, act } from '@testing-library/react'
import { useTodoStore } from './useStore'
describe('useTodoStore', () => {
beforeEach(() => {
useTodoStore.setState({ todos: [], filter: 'all' })
})
it('adds a todo', () => {
const { result } = renderHook(() => useTodoStore())
act(() => {
result.current.addTodo('Test todo')
})
expect(result.current.todos).toHaveLength(1)
expect(result.current.todos[0].text).toBe('Test todo')
expect(result.current.todos[0].completed).toBe(false)
})
it('toggles a todo', () => {
{ result } = ( ())
( {
result..()
})
todoId = result..[].
( {
result..(todoId)
})
(result..[].).()
})
(, {
{ result } = ( ())
( {
result..()
result..()
result..(result..[].)
})
( {
result..()
})
(result..()).()
(result..()[].).()
})
})
Testing Custom Hooks
import { renderHook, act } from '@testing-library/react'
import { useLocalStorage } from './useLocalStorage'
describe('useLocalStorage', () => {
beforeEach(() => {
localStorage.clear()
})
it('returns initial value', () => {
const { result } = renderHook(() => useLocalStorage('key', 'initial'))
expect(result.current[0]).toBe('initial')
})
it('updates value', () => {
const { result } = renderHook(() => useLocalStorage('key', 'initial'))
act(() => {
result.current[1]('updated')
})
expect(result.current[0]).toBe('updated')
expect(localStorage.getItem()).(.())
})
(, {
.(, .())
{ result } = ( (, ))
(result.[]).()
})
})
Best Practices
State Design
- Co-location: Keep state close to where it's used
- Single Source of Truth: Don't duplicate state
- Derived State: Compute from existing state
- Normalized State: Flat structures for relational data
- Immutability: Never mutate state directly
Performance
- Selectors: Subscribe to specific slices
- Memoization: Use useMemo, React.memo
- Lazy Initialization: Defer expensive computations
- Batching: Group state updates
- Code Splitting: Lazy load state modules
Architecture
- Separation of Concerns: UI vs state logic
- Type Safety: TypeScript for all state
- Testability: Pure functions, isolated logic
- Scalability: Modular state organization
- Debugging: DevTools integration
When to Use What
Local State (useState, useReducer)
- UI state (open/closed, hover)
- Form inputs
- Single component only
Context
- Theme, language
- User authentication
- Shared across tree
- Infrequent updates
Zustand
- Global app state
- Simple API needed
- Good DevTools
- Middleware support
Jotai
- Atomic state needs
- Bottom-up architecture
- Granular updates
- TypeScript-first
XState
- Complex workflows
- Explicit states matter
- Visual diagrams needed
- Finite state machines
When to Use This Skill
Activate this skill when you need to:
- Design state architecture for components
- Implement React Context patterns
- Create XState state machines
- Set up Zustand or Jotai stores
- Build custom state hooks
- Optimize state performance
- Test state management logic
- Document state APIs
- Migrate between state solutions
- Debug state issues
Output Format
When implementing state management, provide:
- Complete State Solution: Production-ready code
- Type Definitions: TypeScript types for all state
- API Documentation: How to use the state
- Test Suite: Comprehensive state tests
- Performance Notes: Optimization strategies
- Usage Examples: Real-world integration
Always build state management that is predictable, testable, performant, and maintainable.