React
    Advanced

    Advanced React Hooks Patterns

    Master advanced React Hooks patterns including custom hooks, useReducer, useContext, and performance optimization techniques.

    MMatthew Dhlamini
    120 min
    4.8 (89)
    6 views
    July 19, 2025

    Tutorial Info

    Difficulty:
    Advanced
    Duration:120 minutes
    Rating:
    4.8(89)

    Prerequisites

    • Solid understanding of React basics
    • Experience with useState and useEffect
    • JavaScript ES6+ knowledge
    • TypeScript familiarity

    What You'll Learn

    • Create powerful custom hooks
    • Master useReducer for complex state
    • Implement Context API effectively
    • Optimize component performance
    • Apply advanced patterns in real projects

    Advanced React Hooks Patterns

    In this comprehensive guide, we'll explore advanced React Hooks patterns that will take your React skills to the next level. We'll cover custom hooks, useReducer, useContext, and performance optimization techniques.

    Custom Hooks

    Custom hooks are one of the most powerful features of React Hooks. They allow you to extract component logic into reusable functions.

    Creating a useLocalStorage Hook

    import { useState, useEffect } from 'react'; function useLocalStorage<T>(key: string, initialValue: T) { // Get from local storage then parse stored json or return initialValue const [storedValue, setStoredValue] = useState<T>(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.log(error); return initialValue; } }); // Return a wrapped version of useState's setter function that persists the new value to localStorage const setValue = (value: T | ((val: T) => T)) => { try { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); } catch (error) { console.log(error); } }; return [storedValue, setValue] as const; }

    Using the Custom Hook

    function Profile() { const [name, setName] = useLocalStorage('name', ''); const [email, setEmail] = useLocalStorage('email', ''); return ( <form> <input type="text" placeholder="Name" value={name} onChange={(e) => setName(e.target.value)} /> <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} /> </form> ); }

    useReducer for Complex State

    When state logic is complex, useReducer can be more suitable than useState.

    import React, { useReducer } from 'react'; interface State { count: number; history: number[]; } type Action = | { type: 'increment' } | { type: 'decrement' } | { type: 'reset' } | { type: 'set'; payload: number }; function reducer(state: State, action: Action): State { switch (action.type) { case 'increment': return { count: state.count + 1, history: [...state.history, state.count + 1] }; case 'decrement': return { count: state.count - 1, history: [...state.history, state.count - 1] }; case 'reset': return { count: 0, history: [0] }; case 'set': return { count: action.payload, history: [...state.history, action.payload] }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, { count: 0, history: [0] }); return ( <div> <p>Count: {state.count}</p> <p>History: {state.history.join(', ')}</p> <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> <button onClick={() => dispatch({ type: 'reset' })}>Reset</button> </div> ); }

    Context and useContext

    Context provides a way to pass data through the component tree without prop drilling.

    import React, { createContext, useContext, useReducer, ReactNode } from 'react'; // Theme Context interface ThemeContextType { theme: string; toggleTheme: () => void; } const ThemeContext = createContext<ThemeContextType | undefined>(undefined); function themeReducer(state: { theme: string }, action: { type: string }) { switch (action.type) { case 'TOGGLE_THEME': return { theme: state.theme === 'light' ? 'dark' : 'light' }; default: return state; } } export function ThemeProvider({ children }: { children: ReactNode }) { const [state, dispatch] = useReducer(themeReducer, { theme: 'light' }); const toggleTheme = () => dispatch({ type: 'TOGGLE_THEME' }); return ( <ThemeContext.Provider value={{ theme: state.theme, toggleTheme }}> {children} </ThemeContext.Provider> ); } export function useTheme() { const context = useContext(ThemeContext); if (context === undefined) { throw new Error('useTheme must be used within a ThemeProvider'); } return context; }

    Performance Optimization with useMemo and useCallback

    import React, { useState, useMemo, useCallback } from 'react'; interface Item { id: number; name: string; category: string; } function ExpensiveList({ items }: { items: Item[] }) { const [filter, setFilter] = useState(''); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc'); // Memoize expensive computation const filteredAndSortedItems = useMemo(() => { console.log('Computing filtered and sorted items'); let filtered = items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()) ); return filtered.sort((a, b) => { if (sortOrder === 'asc') { return a.name.localeCompare(b.name); } return b.name.localeCompare(a.name); }); }, [items, filter, sortOrder]); // Memoize callback to prevent unnecessary re-renders const handleItemClick = useCallback((id: number) => { console.log(`Clicked item ${id}`); }, []); return ( <div> <input type="text" placeholder="Filter items..." value={filter} onChange={(e) => setFilter(e.target.value)} /> <button onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}> Sort {sortOrder === 'asc' ? 'Descending' : 'Ascending'} </button> <ul> {filteredAndSortedItems.map(item => ( <li key={item.id} onClick={() => handleItemClick(item.id)}> {item.name} </li> ))} </ul> </div> ); }

    Best Practices

    1. Keep hooks at the top level: Never call hooks inside loops, conditions, or nested functions
    2. Use custom hooks for reusable logic: Extract complex stateful logic into custom hooks
    3. Optimize with useMemo and useCallback: But don't overuse them - measure first
    4. Use useReducer for complex state: When state has multiple sub-values or complex update logic
    5. Provide meaningful names: Custom hooks should start with "use" and be descriptive

    Conclusion

    Advanced React Hooks patterns enable you to write cleaner, more maintainable React code. By mastering custom hooks, useReducer, context, and performance optimization techniques, you can build scalable React applications with confidence.

    Tags

    react
    hooks
    advanced
    performance
    custom-hooks