Back to Blog
6 min read

Mastering React Hooks: Advanced Patterns and Custom Hooks Guide

Unlock the full potential of React Hooks! This comprehensive guide takes you beyond the basics of useState and useEffect to explore advanced patterns, custom hooks, and performance optimization techniques that will elevate your React development skills.

React
Hooks
JavaScript
Frontend

Mastering React Hooks: Beyond useState and useEffect

React Hooks have transformed how we write React components. While most developers are familiar with useState and useEffect, there's a whole world of advanced hooks that can significantly improve your code quality and reusability.

Advanced Built-in Hooks

useReducer

When useState becomes complex, useReducer is your friend:

const initialState = { count: 0 }

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 }
    case 'decrement':
      return { count: state.count - 1 }
    default:
      throw new Error()
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState)
  
  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  )
}

useCallback and useMemo

These hooks help optimize performance by memoizing values and functions:

function ExpensiveComponent({ items, filter }) {
  const expensiveValue = useMemo(() => {
    return items.filter(item => item.category === filter)
  }, [items, filter])

  const handleClick = useCallback((id) => {
    // Handle click logic
  }, [])

  return (
    <div>
      {expensiveValue.map(item => (
        <Item key={item.id} item={item} onClick={handleClick} />
      ))}
    </div>
  )
}

Creating Custom Hooks

Custom hooks are where the real power lies. They allow you to extract component logic into reusable functions.

useLocalStorage Hook

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key)
      return item ? JSON.parse(item) : initialValue
    } catch (error) {
      return initialValue
    }
  })

  const setValue = (value) => {
    try {
      setStoredValue(value)
      window.localStorage.setItem(key, JSON.stringify(value))
    } catch (error) {
      console.error(error)
    }
  }

  return [storedValue, setValue]
}

useFetch Hook

function useFetch(url) {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true)
        const response = await fetch(url)
        const result = await response.json()
        setData(result)
      } catch (err) {
        setError(err)
      } finally {
        setLoading(false)
      }
    }

    fetchData()
  }, [url])

  return { data, loading, error }
}

Best Practices

  1. Keep hooks simple - Each hook should have a single responsibility
  2. Use TypeScript - It makes custom hooks much more reliable
  3. Test your hooks - Use React Testing Library's renderHook
  4. Follow the rules - Always use hooks at the top level

Conclusion

Mastering React hooks goes beyond the basics. By understanding advanced hooks and creating your own custom hooks, you can write more maintainable, reusable, and efficient React code.

The key is to think in terms of reusable logic and extract common patterns into custom hooks that can be shared across your application.