Documentation
Documentation

State Management Guide

Learn how to manage application state using React's Context API and custom hooks.

Context API

React's Context API provides a way to share state between components without prop drilling:

contexts/theme-context.tsxtypescript
1import { createContext, useContext, useReducer, type Dispatch } from 'react'
2
3interface Theme {
4 dark: boolean
5 color: string
6}
7
8type Action =
9 | { type: 'TOGGLE_THEME' }
10 | { type: 'SET_COLOR'; payload: string }
11
12interface State {
13 theme: Theme
14}
15
16interface ThemeContextType {
17 state: State
18 dispatch: Dispatch<Action>
19}
20
21const initialState: State = {
22 theme: {
23 dark: false,
24 color: 'blue',
25 },
26}
27
28const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
29
30function themeReducer(state: State, action: Action): State {
31 switch (action.type) {
32 case 'TOGGLE_THEME':
33 return {
34 ...state,
35 theme: {
36 ...state.theme,
37 dark: !state.theme.dark,
38 },
39 }
40 case 'SET_COLOR':
41 return {
42 ...state,
43 theme: {
44 ...state.theme,
45 color: action.payload,
46 },
47 }
48 default:
49 return state
50 }
51}

Custom Providers

Create custom providers to manage specific state domains:

app/layout.tsxtypescript
1'use client'
2
3import { createContext, useReducer, useContext, type ReactNode } from 'react'
4
5export function ThemeProvider({ children }: { children: ReactNode }) {
6 const [state, dispatch] = useReducer(themeReducer, initialState)
7
8 return (
9 <ThemeContext.Provider value={{ state, dispatch }}>
10 {children}
11 </ThemeContext.Provider>
12 )
13}
14
15// Usage in layout
16export default function RootLayout({
17 children,
18}: {
19 children: React.ReactNode
20}) {
21 return (
22 <html lang="en">
23 <body>
24 <ThemeProvider>
25 {children}
26 </ThemeProvider>
27 </body>
28 </html>
29 )
30}

Custom Hooks

Create custom hooks to access and modify context state:

hooks/use-theme.tstypescript
1export function useTheme() {
2 const context = useContext(ThemeContext)
3 if (context === undefined) {
4 throw new Error('useTheme must be used within a ThemeProvider')
5 }
6 return context
7}
8
9// Custom hook for specific theme actions
10export function useThemeActions() {
11 const { dispatch } = useTheme()
12
13 const toggleTheme = () => {
14 dispatch({ type: 'TOGGLE_THEME' })
15 }
16
17 const setColor = (color: string) => {
18 dispatch({ type: 'SET_COLOR', payload: color })
19 }
20
21 return {
22 toggleTheme,
23 setColor,
24 }
25}

Examples

Here's how to use the context and hooks in your components:

components/theme-components.tsxtypescript
1'use client'
2
3import { useTheme, useThemeActions } from '@/hooks/use-theme'
4
5export function ThemeToggle() {
6 const { state } = useTheme()
7 const { toggleTheme, setColor } = useThemeActions()
8
9 return (
10 <div className="space-y-4">
11 <button
12 onClick={toggleTheme}
13 className="rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium"
14 >
15 {state.theme.dark ? 'Switch to Light' : 'Switch to Dark'}
16 </button>
17
18 <div className="flex gap-2">
19 {['blue', 'green', 'red'].map((color) => (
20 <button
21 key={color}
22 onClick={() => setColor(color)}
23 className={`h-8 w-8 rounded-full ${
24 state.theme.color === color ? 'ring-2 ring-offset-2' : ''
25 }`}
26 style={{ backgroundColor: color }}
27 />
28 ))}
29 </div>
30 </div>
31 )
32}
33
34// Using the theme values
35export function ThemedComponent() {
36 const { state } = useTheme()
37
38 return (
39 <div
40 className={`rounded-lg p-4 ${
41 state.theme.dark ? 'bg-gray-900 text-white' : 'bg-white text-gray-900'
42 }`}
43 style={{ borderColor: state.theme.color }}
44 >
45 <h2>Themed Content</h2>
46 <p>This component respects the current theme settings.</p>
47 </div>
48 )
49}