stackademic

The leading education platform for anyone with an interest in software development.

useCallback

Memoize function references to optimize child re-renders

Overview

useCallback returns a stable function reference between renders when dependencies are unchanged. It helps React.memo children avoid re-rendering because a new inline function was passed as a prop.

Syntax / Usage

import { useCallback, useState, memo } from 'react'

const ListItem = memo(function ListItem({ item, onSelect }) {
  return (
    <button onClick={() => onSelect(item.id)}>
      {item.name}
    </button>
  )
})

function ItemList({ items }) {
  const [selected, setSelected] = useState(null)

  const handleSelect = useCallback((id) => {
    setSelected(id)
  }, [])

  return items.map((item) => (
    <ListItem key={item.id} item={item} onSelect={handleSelect} />
  ))
}

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

Examples

Pass stable handler to a virtualized list:

const onRowClick = useCallback((rowIndex) => {
  setActiveRow(rowIndex)
}, [])

Debounce wrapper with stable identity:

const debouncedSearch = useCallback(
  debounce((term) => fetchResults(term), 300),
  [] // debounce instance created once
)

Common Mistakes

  • Wrapping every function in useCallback without a memoized child—adds cost, no benefit
  • Stale closures from incomplete dependency arrays
  • Assuming useCallback prevents the function body from running—it only stabilizes the reference
  • Pairing with React.memo on children but passing unstable object props

See Also

use-memo use-effect props components