Learn UseCallback, useRef & useMemo

Jayant VermaJayant Verma
4 min read

🚀 Unlocking React Superpowers: useCallback, useRef, and useMemo — Explained Like You're 5 (Almost)

Let’s be real: hooks like useCallback, useMemo, and useRef can feel like React’s secret spells — powerful, but confusing if you don’t know when or how to cast them.

In this post, we’ll demystify these hooks, explain when to use them (and when not to), and show you how they help your app stay fast, clean, and smart.


🧠 useCallback: The Memory Saver for Functions

🤔 What’s the Problem?

Every time a component re-renders, all its inner functions are reborn. Yes, even that innocent little arrow function you wrote inside the component. And here’s the kicker: if you’re passing those functions to child components, React will think they’ve changed—even if the logic is the same!

This can lead to unnecessary re-renders, especially if you're using React.memo() in child components. That's wasted work. And nobody likes wasted work.

🛠️ The Fix: useCallback

useCallback tells React:

“Hey, if these dependencies haven’t changed, don’t bother creating a new version of this function. Just reuse the last one.”

🧪 Example

const handleClick = useCallback(() => {
  console.log("Button clicked!");
}, []);

Now handleClick will stay exactly the same across renders — unless you change its dependencies.

✅ When to Use

  • Passing stable functions to React.memo() children

  • Debouncing or throttling callbacks

  • Avoiding infinite loops in useEffect


🪄 useRef: Your Secret Pocket for Storing Things

🧠 What Is It?

Imagine a little box attached to your component that you can put things into — and they stay there forever (well, for the life of the component).

That’s useRef.

Unlike useState, updating a ref doesn’t cause a re-render. It just quietly holds onto values behind the scenes.

  • DOM Access:

      const inputRef = useRef(null);
      inputRef.current.focus();
    
  • Timers and Intervals

  • Keeping values alive between renders (like the previous value of a prop)

🔥 Bonus

The object returned by useRef() is stable. It never changes between renders — perfect for keeping track of things without triggering React’s attention.


🧮 useMemo: Stop Recalculating the Obvious

🧠 What’s Going On?

Imagine calculating the square root of 5000 numbers every time your component renders. Not ideal, right?

That’s what React does unless you tell it not to. useMemo helps you remember expensive calculations, so React doesn’t redo them unless it truly has to.

📦 The Memoized Value

const expensiveResult = useMemo(() => {
  return heavyComputation(input);
}, [input]);

React runs the function once — and only re-runs it if input changes. That’s performance magic right there.

✅ When to Use

  • Expensive loops, filters, or calculations

  • Derived values from state/props

  • Preventing unnecessary re-renders in deeply nested components


🥊 useCallback vs. useMemo: What’s the Difference?

This is the big one! Let’s clear the confusion once and for all.

ConceptuseCallbackuseMemo
It remembers…A functionA value
Returns a…Memoized functionMemoized value
You use it forKeeping function references stableAvoiding re-computation of heavy logic
Real-world useClick handlers, API triggersFiltered lists, derived state

👉 Think of useCallback(fn) as useMemo(() => fn). Technically, it’s the same behavior — just focused on functions, not results.


🤯 Putting It All Together: A Practical Combo

const SearchBox = ({ query }) => {
  const inputRef = useRef();
  const [results, setResults] = useState([]);

  const fetchResults = useCallback(() => {
    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(setResults);
  }, [query]);

  const filtered = useMemo(() => {
    return results.filter(item => item.active);
  }, [results]);

  useEffect(() => {
    fetchResults();
  }, [fetchResults]);

  return (
    <div>
      <input ref={inputRef} placeholder="Search..." />
      <ul>
        {filtered.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};

Here’s what’s happening:

  • useRef grabs the DOM input (no re-render).

  • useCallback ensures the fetch function doesn’t get recreated every render.

  • useMemo avoids filtering the same list over and over again.

Result: Smarter code, fewer renders, faster UI.


🧨 Common Pitfalls

  1. Overusing these hooks
    Don’t optimize for the sake of it. Only use them if you see performance issues or unstable references.

  2. Empty dependency arrays where they shouldn’t be
    If you freeze a value that should update (but don’t include its dependencies), things break.

  3. Expecting useRef to re-render things
    Nope. If you want reactivity, use useState.


✨ Final Thoughts

React hooks are like tools in a toolbox — and useCallback, useMemo, and useRef are some of the sharpest ones. But like any sharp tool, you only use them when they solve a real problem.

Don’t memorize these hooks — understand them.

And now that you do, your React code is going to be faster, cleaner, and way more optimized.


0
Subscribe to my newsletter

Read articles from Jayant Verma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jayant Verma
Jayant Verma