useCallback vs useMemo: A Deep Dive into React Performance Hooks

React provides two powerful hooks for optimizing component performance: useCallback and useMemo. While they might seem similar at first glance, they serve different purposes and have distinct behaviors. Let's explore their differences and use cases.

Quick Comparison

Parameters and Return Values

AspectuseCallbackuseMemo
Parameters(fn, dependencies)(computeValue, dependencies)
First ParameterFunction to memoizeFunction that returns value to memoize
Second ParameterArray of dependenciesArray of dependencies
Return ValueMemoized functionMemoized computed value
Type of ReturnFunction referenceAny value (number, object, array, etc.)

Behavior and Usage

AspectuseCallbackuseMemo
When to UseWhen passing callbacks to child componentsWhen computing expensive values
Re-creation TriggerWhen dependencies changeWhen dependencies change
Performance ImpactPrevents child component re-rendersPrevents expensive recalculations
Memory UsageStores function referenceStores computed value
Common Use CasesEvent handlers, callbacks, effect dependenciesComplex calculations, object creation, data transformations

Technical Details

AspectuseCallbackuseMemo
Reference StabilityMaintains same function referenceMaintains same value reference
Dependency ChangesCreates new function if dependencies changeRecomputes value if dependencies change
Render ImpactNo re-render on function changeNo re-render on value change
Optimization LevelFunction-level memoizationValue-level memoization
Best Used WithReact.memo() componentsExpensive computations

What is useCallback?

useCallback is a React Hook that lets you cache a function definition between re-renders. It takes two parameters:

  1. A function that you want to cache
  2. An array of dependencies that the function depends on
const cachedFn = useCallback(fn, dependencies)

How useCallback Works

When you wrap a function with useCallback, React will:

  • Return the same function instance between renders if dependencies haven't changed
  • Create a new function instance if any dependency has changed
  • Cache the function definition until dependencies change
function ProductPage({ productId, referrer }) {
  const handleSubmit = useCallback(
    (orderDetails) => {
      post('/product/' + productId + '/buy', {
        referrer,
        orderDetails,
      })
    },
    [productId, referrer],
  )

  return <ShippingForm onSubmit={handleSubmit} />
}

What is useMemo?

useMemo is a React Hook that lets you cache the result of a computation between re-renders. It also takes two parameters:

  1. A function that returns what you want to cache
  2. An array of dependencies that the computation depends on
const cachedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])

How useMemo Works

When you wrap a computation with useMemo, React will:

  • Return the cached value between renders if dependencies haven't changed
  • Recompute the value if any dependency has changed
  • Cache the computed value until dependencies change
function ExpensiveComponent({ items }) {
  const sortedItems = useMemo(() => {
    return items.sort((a, b) => b.price - a.price)
  }, [items])

  return (
    <ul>
      {sortedItems.map((item) => (
        <li key={item.id}>
          {item.name}: ${item.price}
        </li>
      ))}
    </ul>
  )
}

Key Differences

1. Purpose

useCallback:

  • Caches function definitions
  • Used for optimizing child components that receive callbacks as props
  • Prevents unnecessary re-renders of child components that depend on these callbacks
function ParentComponent() {
  const [count, setCount] = useState(0)

  // Without useCallback, this function would be recreated on every render
  const handleClick = useCallback(() => {
    console.log('Button clicked')
  }, []) // Empty dependency array since it doesn't depend on any values

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  )
}

useMemo:

  • Caches computed values
  • Used for optimizing expensive computations
  • Prevents unnecessary recalculations of complex values
function ExpensiveComponent({ data }) {
  // Without useMemo, this expensive calculation would run on every render
  const processedData = useMemo(() => {
    return data.map((item) => ({
      ...item,
      total: item.price * item.quantity,
      tax: item.price * 0.1,
    }))
  }, [data])

  return <DataTable data={processedData} />
}

2. Return Value

useCallback:

  • Returns a memoized function
  • The function reference remains stable between renders
  • Useful for callback props in child components
function SearchComponent({ onSearch }) {
  const [query, setQuery] = useState('')

  const handleSearch = useCallback(() => {
    onSearch(query)
  }, [query, onSearch])

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <button onClick={handleSearch}>Search</button>
    </div>
  )
}

useMemo:

  • Returns a memoized value
  • The computed value remains stable between renders
  • Useful for expensive calculations or complex object creation
function ChartComponent({ data }) {
  const chartData = useMemo(() => {
    return {
      labels: data.map((item) => item.date),
      datasets: [
        {
          data: data.map((item) => item.value),
          backgroundColor: 'rgba(75, 192, 192, 0.2)',
          borderColor: 'rgba(75, 192, 192, 1)',
          borderWidth: 1,
        },
      ],
    }
  }, [data])

  return <LineChart data={chartData} />
}

When to Use Each

Use useCallback When:

  1. Passing callbacks to optimized child components:
const MemoizedChild = memo(function Child({ onClick }) {
  return <button onClick={onClick}>Click me</button>
})

function Parent() {
  const handleClick = useCallback(() => {
    console.log('Clicked')
  }, [])

  return <MemoizedChild onClick={handleClick} />
}
  1. Using callbacks in useEffect dependencies:
function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('')

  const createOptions = useCallback(() => {
    return {
      serverUrl: 'https://localhost:1234',
      roomId: roomId,
    }
  }, [roomId])

  useEffect(() => {
    const options = createOptions()
    const connection = createConnection(options)
    connection.connect()
    return () => connection.disconnect()
  }, [createOptions])
}

Use useMemo When:

  1. Computing expensive values:
function ExpensiveComponent({ items }) {
  const total = useMemo(() => {
    return items.reduce((sum, item) => {
      return sum + item.price * item.quantity
    }, 0)
  }, [items])

  return <div>Total: ${total}</div>
}
  1. Creating complex objects:
function Chart({ data }) {
  const chartConfig = useMemo(
    () => ({
      type: 'line',
      data: {
        labels: data.map((d) => d.date),
        datasets: [
          {
            data: data.map((d) => d.value),
            backgroundColor: 'rgba(75, 192, 192, 0.2)',
            borderColor: 'rgba(75, 192, 192, 1)',
            borderWidth: 1,
          },
        ],
      },
      options: {
        responsive: true,
        plugins: {
          legend: {
            position: 'top',
          },
        },
      },
    }),
    [data],
  )

  return <ChartComponent config={chartConfig} />
}

Performance Considerations

Advantages

FeatureuseCallbackuseMemo
Primary PurposeCaches function definitionsCaches computed values
Main BenefitPrevents unnecessary re-renders of child componentsPrevents expensive recalculations
Reference StabilityMaintains referential equality of functionsMaintains referential equality of computed values
Use CasesOptimizing callback props, preventing infinite effect loopsOptimizing expensive computations, preventing unnecessary re-renders

Disadvantages

FeatureuseCallbackuseMemo
OverheadAdds overhead for simple functionsAdds overhead for simple computations
ComplexityCan be overused, leading to unnecessary complexityCan be overused, leading to memory bloat
EffectivenessMay not provide benefits for non-optimized componentsMay not provide benefits for simple values
MaintenanceRequires careful dependency managementRequires careful dependency management

Best Practices

  1. Don't Overuse:
// ❌ Bad: Unnecessary useCallback
const handleClick = useCallback(() => {
  console.log('Clicked')
}, [])

// ✅ Good: Simple function doesn't need memoization
const handleClick = () => {
  console.log('Clicked')
}
  1. Use with memo:
// ✅ Good: useCallback with memo
const MemoizedButton = memo(function Button({ onClick }) {
  return <button onClick={onClick}>Click me</button>
})

function Parent() {
  const handleClick = useCallback(() => {
    console.log('Clicked')
  }, [])

  return <MemoizedButton onClick={handleClick} />
}
  1. Proper Dependency Management:
// ❌ Bad: Missing dependencies
const handleSubmit = useCallback(() => {
  submitForm(formData)
}, []) // Missing formData dependency

// ✅ Good: Proper dependency array
const handleSubmit = useCallback(() => {
  submitForm(formData)
}, [formData])

Common Pitfalls

  1. Incorrect Dependencies:
// ❌ Bad: Missing dependencies
const handleClick = useCallback(() => {
  console.log(count) // count is used but not in dependencies
}, [])

// ✅ Good: Include all dependencies
const handleClick = useCallback(() => {
  console.log(count)
}, [count])
  1. Unnecessary Memoization:
// ❌ Bad: Memoizing simple values
const simpleValue = useMemo(() => 1 + 1, [])

// ✅ Good: Direct value assignment
const simpleValue = 2

Conclusion

Understanding when to use useCallback and useMemo is crucial for optimizing React component performance. While both hooks help prevent unnecessary re-renders and computations, they serve different purposes:

  • Use useCallback for memoizing function definitions, especially when passing callbacks to optimized child components
  • Use useMemo for memoizing computed values, especially for expensive calculations or complex object creation

Remember that these hooks come with their own overhead, so use them judiciously and only when they provide clear performance benefits.

References

  1. Meta Platforms, Inc. (2024). useCallback Hook. React Documentation. https://react.dev/reference/react/useCallback

  2. Meta Platforms, Inc. (2024). useMemo Hook. React Documentation. https://react.dev/reference/react/useMemo

  3. DeadSimpleChat. (2023). React useMemo vs useCallback: A Complete Guide. https://deadsimplechat.com/blog/react-usememo-vs-usecallback/

  4. Developer Way. (2023). How to use memo and useCallback. https://www.developerway.com/posts/how-to-use-memo-use-callback

  5. Refine. (2023). React useCallback Guide with Examples. https://refine.dev/blog/react-usecallback-guide/#example