Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /React
  4. /Usecallback Hook
Your Progress0%
0 of 42 completed

React Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App

useCallback Hook

Memoizing functions to prevent re-renders

Functions in JavaScript are objects, and when you define a function inside a component, React creates a new function on every render. This can cause performance problems when you pass functions as props to child components - they see a "new" function each time and re-render unnecessarily. The useCallback hook solves this by memoizing functions, ensuring they maintain the same reference across renders. Let's master function optimization! 🎯

The Problem: New Functions Every Render

Without useCallback, functions are recreated on every render:

problem-without-callback.jsx
// Child component wrapped in React.memo
const ExpensiveChild = React.memo(({ onClick, data }) => {
  console.log('ExpensiveChild rendered')
  return <button onClick={onClick}>{data}</button>
})

function Parent() {
  const [count, setCount] = React.useState(0)
  const [text, setText] = React.useState('')
  
  // ❌ Problem: New function created every render!
  const handleClick = () => {
    console.log('Clicked!')
  }
  
  return (
    <div>
      <input value={text} onChange={e => setText(e.target.value)} />
      <p>Count: {count}</p>
      <ExpensiveChild onClick={handleClick} data="Click me" />
    </div>
  )
}

// Problem:
// 1. User types in input
// 2. Parent re-renders
// 3. handleClick is recreated (new function reference)
// 4. ExpensiveChild sees "different" prop
// 5. ExpensiveChild re-renders (even though React.memo is used!)
// 6. React.memo can't prevent re-render because onClick changed

Why Functions Get Recreated:

Every time a component renders, all code inside runs again.

Function declarations/expressions create NEW function objects:
const fn = () => {} ← New function every render

In JavaScript: () => {} !== () => {}
Even if they do the same thing!

The Solution: useCallback

useCallback memoizes the function, keeping the same reference:

solution-with-callback.jsx
const ExpensiveChild = React.memo(({ onClick, data }) => {
  console.log('ExpensiveChild rendered')
  return <button onClick={onClick}>{data}</button>
})

function Parent() {
  const [count, setCount] = React.useState(0)
  const [text, setText] = React.useState('')
  
  // ✅ Solution: Memoize the function
  const handleClick = React.useCallback(() => {
    console.log('Clicked!')
  }, [])  // Empty array = same function forever
  
  return (
    <div>
      <input value={text} onChange={e => setText(e.target.value)} />
      <p>Count: {count}</p>
      <ExpensiveChild onClick={handleClick} data="Click me" />
    </div>
  )
}

// How it works:
// 1. First render: Function created, reference stored
// 2. User types in input, Parent re-renders
// 3. useCallback returns same function reference
// 4. ExpensiveChild sees "same" prop
// 5. React.memo prevents re-render (prop didn't change!)
// 6. Performance optimized!

useCallback Syntax

usecallback-syntax.jsx
const memoizedCallback = React.useCallback(
  () => {
    // Function code
    doSomething(a, b)
  },
  [a, b]  // Dependencies - recreate when these change
)

// Parts:
// 1. Callback function - The function to memoize
// 2. Dependency array - When to recreate the function

// Example with dependencies:
const handleClick = React.useCallback(() => {
  console.log('Count is:', count)
}, [count])  // Recreate when count changes

// Without dependencies (stable function):
const handleClick = React.useCallback(() => {
  console.log('Clicked!')
}, [])  // Never recreated

🤔 useCallback vs useMemo

useCallback: Returns a memoized FUNCTION
const fn = useCallback(() => doSomething(), [deps])

useMemo: Returns a memoized VALUE
const value = useMemo(() => calculate(), [deps])

Actually, useCallback(fn, deps) is equivalent to useMemo(() => fn, deps)

useCallback is just a convenience wrapper!

Common useCallback Use Cases

1. Optimizing React.memo Components

use-case-memo.jsx
// Memoized child component
const TodoItem = React.memo(({ todo, onToggle, onDelete }) => {
  console.log('TodoItem rendered:', todo.id)
  
  return (
    <div>
      <input 
        type="checkbox"
        checked={todo.done}
        onChange={() => onToggle(todo.id)}
      />
      <span>{todo.text}</span>
      <button onClick={() => onDelete(todo.id)}>Delete</button>
    </div>
  )
})

function TodoList() {
  const [todos, setTodos] = React.useState([
    { id: 1, text: 'Learn React', done: false },
    { id: 2, text: 'Build project', done: false }
  ])
  const [filter, setFilter] = React.useState('all')
  
  // Memoize callbacks to prevent TodoItem re-renders
  const handleToggle = React.useCallback((id) => {
    setTodos(todos => todos.map(todo =>
      todo.id === id ? { ...todo, done: !todo.done } : todo
    ))
  }, [])  // No dependencies, uses functional update
  
  const handleDelete = React.useCallback((id) => {
    setTodos(todos => todos.filter(todo => todo.id !== id))
  }, [])
  
  return (
    <div>
      <select value={filter} onChange={e => setFilter(e.target.value)}>
        <option value="all">All</option>
        <option value="active">Active</option>
        <option value="done">Done</option>
      </select>
      
      {todos.map(todo => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={handleToggle}
          onDelete={handleDelete}
        />
      ))}
    </div>
  )
}

// When filter changes, TodoItems don't re-render!
// Because onToggle and onDelete keep same reference

2. Event Handlers with Dependencies

use-case-dependencies.jsx
function SearchBox({ onSearch }) {
  const [query, setQuery] = React.useState('')
  const [category, setCategory] = React.useState('all')
  
  // Memoize search handler with dependencies
  const handleSearch = React.useCallback(() => {
    onSearch({ query, category })
  }, [query, category, onSearch])  // Recreate when these change
  
  return (
    <div>
      <input 
        value={query}
        onChange={e => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <select value={category} onChange={e => setCategory(e.target.value)}>
        <option value="all">All</option>
        <option value="products">Products</option>
        <option value="users">Users</option>
      </select>
      <button onClick={handleSearch}>Search</button>
    </div>
  )
}

// handleSearch is recreated only when query, category, or onSearch change

3. useEffect Dependencies

use-case-effect.jsx
function DataFetcher({ userId }) {
  const [data, setData] = React.useState(null)
  
  // Memoize fetch function
  const fetchData = React.useCallback(async () => {
    const response = await fetch(`/api/users/${userId}/data`)
    const data = await response.json()
    setData(data)
  }, [userId])  // Recreate when userId changes
  
  React.useEffect(() => {
    fetchData()
  }, [fetchData])  // Safe to include fetchData in dependencies
  
  return <div>{data?.name}</div>
}

// Without useCallback:
// - fetchData recreated every render
// - useEffect runs every render
// - Infinite loop!
//
// With useCallback:
// - fetchData stable unless userId changes
// - useEffect runs only when needed

4. Custom Hooks with Callbacks

use-case-custom-hook.jsx
function useDebounce(callback, delay) {
  const timeoutRef = React.useRef(null)
  
  // Memoize debounced function
  const debouncedCallback = React.useCallback((...args) => {
    clearTimeout(timeoutRef.current)
    
    timeoutRef.current = setTimeout(() => {
      callback(...args)
    }, delay)
  }, [callback, delay])
  
  return debouncedCallback
}

// Usage:
function SearchBox() {
  const [query, setQuery] = React.useState('')
  
  const handleSearch = React.useCallback((value) => {
    console.log('Searching for:', value)
    // API call here
  }, [])
  
  const debouncedSearch = useDebounce(handleSearch, 500)
  
  return (
    <input
      value={query}
      onChange={(e) => {
        setQuery(e.target.value)
        debouncedSearch(e.target.value)
      }}
    />
  )
}

Interactive Demo

ReactPlayground.jsx
Code Editor
Preview

💡 Tip: Edit the code above and click "Run" to see your changes

When to Use useCallback

✅ DO Use useCallback When:

  • Passing to React.memo components - Prevent unnecessary re-renders
  • Using in useEffect dependencies - Avoid infinite loops
  • Passing to child components - When child is expensive to render
  • Creating custom hooks - Return stable callback references
  • Optimizing lists - Item callbacks in mapped lists

❌ DON'T Use useCallback When:

  • Function is only used internally - Not passed as prop
  • Child doesn't use React.memo - No benefit
  • Simple event handlers - onClick for basic buttons
  • Everywhere "just in case" - useCallback has overhead too

useCallback Only Works With React.memo:

useCallback alone doesn't prevent re-renders!

You need BOTH:
1. useCallback in parent (stable function reference)
2. React.memo on child (skip render if props same)

Without React.memo, child re-renders anyway, and useCallback adds overhead for nothing.

Common useCallback Mistakes

1. Missing Dependencies

mistake-deps.jsx
function Component() {
  const [count, setCount] = React.useState(0)
  
  // ❌ Wrong - Missing count dependency
  const handleClick = React.useCallback(() => {
    console.log('Count is:', count)  // Uses count
  }, [])  // Missing count!
  
  // Result: Logs stale count value
  
  // ✅ Correct - Include all dependencies
  const handleClick = React.useCallback(() => {
    console.log('Count is:', count)
  }, [count])  // Include count
  
  // ✅ Better - Use functional update (no dependency)
  const increment = React.useCallback(() => {
    setCount(c => c + 1)  // Doesn't use count from closure
  }, [])  // No dependencies needed!
}

2. Using Without React.memo

mistake-no-memo.jsx
// ❌ Bad - useCallback without React.memo
function Parent() {
  const handleClick = React.useCallback(() => {
    console.log('Clicked')
  }, [])
  
  return <Child onClick={handleClick} />  // Child not memoized!
}

function Child({ onClick }) {
  // Re-renders anyway because not wrapped in React.memo
  return <button onClick={onClick}>Click</button>
}

// ✅ Good - Use React.memo with useCallback
const Child = React.memo(function Child({ onClick }) {
  return <button onClick={onClick}>Click</button>
})

// Now useCallback actually helps!

3. Overusing useCallback

mistake-overuse.jsx
// ❌ Bad - Overusing for simple handlers
function Component() {
  const [count, setCount] = React.useState(0)
  
  const increment = React.useCallback(() => {
    setCount(c => c + 1)
  }, [])  // Unnecessary!
  
  const decrement = React.useCallback(() => {
    setCount(c => c - 1)
  }, [])  // Unnecessary!
  
  return (
    <div>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  )
}

// ✅ Good - No memoization needed for simple handlers
function Component() {
  const [count, setCount] = React.useState(0)
  
  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>+</button>
      <button onClick={() => setCount(c => c - 1)}>-</button>
    </div>
  )
}

// No child components, no React.memo, no need for useCallback!

4. Circular Dependencies

mistake-circular.jsx
// ❌ Problematic - Circular dependency
function Component() {
  const [count, setCount] = React.useState(0)
  
  const increment = React.useCallback(() => {
    setCount(count + 1)  // Uses count
  }, [count])  // Depends on count
  
  React.useEffect(() => {
    increment()  // Calls increment
  }, [increment])  // Depends on increment
}

// Problem: count changes → increment recreated → effect runs → count changes...

// ✅ Solution 1: Use functional update
const increment = React.useCallback(() => {
  setCount(c => c + 1)  // No count dependency
}, [])

// ✅ Solution 2: Remove useCallback if not needed
React.useEffect(() => {
  setCount(c => c + 1)
}, [])  // Just do it directly

useCallback Patterns

Pattern 1: Optimized List Items

pattern-list.jsx
const ListItem = React.memo(({ item, onDelete, onEdit }) => {
  return (
    <div>
      <span>{item.name}</span>
      <button onClick={() => onEdit(item.id)}>Edit</button>
      <button onClick={() => onDelete(item.id)}>Delete</button>
    </div>
  )
})

function ItemList({ items }) {
  const handleDelete = React.useCallback((id) => {
    // Delete logic using functional update
    setItems(items => items.filter(item => item.id !== id))
  }, [])
  
  const handleEdit = React.useCallback((id) => {
    // Edit logic
    console.log('Editing item:', id)
  }, [])
  
  return (
    <div>
      {items.map(item => (
        <ListItem
          key={item.id}
          item={item}
          onDelete={handleDelete}
          onEdit={handleEdit}
        />
      ))}
    </div>
  )
}

// Only items that actually change re-render!

Pattern 2: Event Handler Factory

pattern-factory.jsx
function Form() {
  const [values, setValues] = React.useState({ name: '', email: '' })
  
  // Factory pattern: Create handler for any field
  const handleChange = React.useCallback((field) => {
    return (e) => {
      setValues(v => ({ ...v, [field]: e.target.value }))
    }
  }, [])
  
  return (
    <form>
      <input value={values.name} onChange={handleChange('name')} />
      <input value={values.email} onChange={handleChange('email')} />
    </form>
  )
}

// Single memoized handler, multiple fields!

Pattern 3: Combining with useRef

pattern-ref.jsx
function Component({ onSave }) {
  const [data, setData] = React.useState({})
  const onSaveRef = React.useRef(onSave)
  
  // Keep ref up to date
  React.useEffect(() => {
    onSaveRef.current = onSave
  }, [onSave])
  
  // Stable callback that always calls latest onSave
  const handleSave = React.useCallback(() => {
    onSaveRef.current(data)
  }, [data])  // Only depends on data, not onSave
  
  return <button onClick={handleSave}>Save</button>
}

// Useful when onSave prop changes frequently

useCallback vs useMemo vs useRef

comparison.jsx
function Component() {
  const [count, setCount] = React.useState(0)
  
  // useCallback - Memoize FUNCTION
  const handleClick = React.useCallback(() => {
    console.log(count)
  }, [count])
  
  // useMemo - Memoize VALUE
  const doubled = React.useMemo(() => {
    return count * 2
  }, [count])
  
  // useRef - Mutable value (doesn't trigger re-render)
  const renderCount = React.useRef(0)
  renderCount.current += 1
  
  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <p>Renders: {renderCount.current}</p>
      <button onClick={handleClick}>Click</button>
    </div>
  )
}

// Summary:
// useCallback: () => value   (memoized function)
// useMemo:     value          (memoized value)
// useRef:      { current }    (mutable container)

Key Takeaways

  • useCallback memoizes functions
  • Prevents new function references on every render
  • Works best with React.memo components
  • Returns a memoized FUNCTION (not value)
  • Use for callbacks passed to optimized child components
  • Include all dependencies in the array
  • Use functional updates to avoid dependencies
  • Don't use without React.memo (no benefit, just overhead)
  • Essential for useEffect dependencies with functions
  • useCallback(fn, deps) = useMemo(() => fn, deps)
  • Measure performance before optimizing
  • Avoid overuse - adds complexity and overhead
  • Completes the optimization toolkit with useMemo

What's Next?

Congratulations! You've completed the Advanced Hooks section. You now understand:

  • useRef for DOM access and mutable values
  • useContext for sharing state without prop drilling
  • useMemo for memoizing expensive calculations
  • useCallback for memoizing functions

You have a complete toolkit for building optimized, professional React applications! You know how to access DOM elements, share global state, and optimize performance with memoization. But there's still more to explore:

  • Custom Hooks - Creating reusable logic
  • React Patterns - Composition, render props, HOCs
  • Testing - Unit and integration testing
  • Advanced Patterns - Portals, error boundaries, Suspense
  • And much more!

Keep building - you're well on your way to React mastery! 🎉

Learning how to memoize functions with useCallback for React performance optimization!

Previous
useMemo Hook
Next
Custom Hooks

Never Miss a New React Tutorial

Join 2,000+ developers learning React step-by-step. Get new tutorials, tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

React Tutorials

0 of 42 completed

Your Progress0%

Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo