avatar
Stop Using useMemo and useCallback — There’s use() Now
June 29, 2025
5 min read

Stop Using useMemo and useCallback — There’s use() Now

🧠 Stop Using useMemo (Most of the Time)

React gives us two powerful hooks for memoization: useMemo and useCallback. But many developers reach for useMemo prematurely, thinking it's optimizing performance.

Spoiler: It's not. In most cases, you should be reaching for useCallback — or nothing at all.

🚨 The Problem with useMemo

  • It doesn't cache across renders — only within the same render instance.
  • It can make performance worse due to unnecessary diffing logic.
  • It adds noise to the codebase, making it harder to read and debug.

✅ Why useCallback Is Often the Better Choice

useCallback is essentially useMemo for functions — and functions are what usually trigger re-renders in child components.

const handleClick = () => {
    console.log("clicked");
  };
  
  // Every re-render creates a new function reference
  
  const handleClick = useCallback(() => {
    console.log("clicked");
  }, []);
  // Stable reference between renders
  

💡 When useMemo Actually Makes Sense

Use useMemo only when:

  • You're computing an expensive derived value, and
  • That value is used in dependencies or passed to memoized children.
const filteredData = useMemo(() => {
    return data.filter(item => item.active);
  }, [data]);
  

❌ A Common Mistake

const fullName = useMemo(() => {
    return `${user.firstName} ${user.lastName}`;
  }, [user.firstName, user.lastName]);
  

✅ Just do:

const fullName = `${user.firstName} ${user.lastName}`;
  

🔁 When useCallback Shines

const onToggle = useCallback(() => {
    setOpen(prev => !prev);
  }, []);
  

Without useCallback, this function gets re-created on every render, causing unnecessary re-renders in children that receive it as a prop.

🧼 Final Advice

  • Don't use useMemo unless profiling shows a real issue.
  • Use useCallback when memoized children depend on functions.
  • Memoization is a performance tool — not a default coding pattern.

🆕 Enter use() – The Game Changer

In Server Components, use() gives us automatic memoization of resolved values — no need to manually optimize with useMemo or useCallback.

Because use() runs once per request on the server, its result is effectively cached across renders — making it memoized by default.

📌 Real-World Example:

async function getUser(id) {
    const res = await fetch(`${API_URL}/users/${id}`);
    return res.json();
  }
  
  export default function UserProfile({ userId }) {
    const user = use(getUser(userId));
  
    return (
      <div>
        <h2>{user.name}</h2>
        <p>{user.bio}</p>
      </div>
    );
  }
  

🧱 Side-by-Side Comparison

Feature useMemo useCallback use()
Purpose Memoize values Memoize functions Resolve promises
Scope Client Components Client Components Server Components (RSC)
Automatic Memo? No No Yes
Caching Only within single render Only within single render Across renders (per request)
Best For Derived values Event handlers Async data resolution

🧠 When Should You Still Use Them?

Even with use() around, there are still valid use cases for useMemo and useCallback:

✅ Keep Using useMemo When:

  • You're deriving complex values (like sorting, filtering large arrays)
  • You pass the value to useEffect, useContext, or useReducer
  • You're using it in React.memo to preserve referential equality

✅ Keep Using useCallback When:

  • You pass the function to React.memo wrapped components
  • You're using it as a dependency in useEffect
  • You're creating event handlers in dynamic contexts

🧼 Final Thoughts

Rule of thumb:
- Use use() in Server Components for async data.
- Use useCallback when passing functions to memoized children.
- Use useMemo only when profiling shows performance issues.
- Otherwise, keep it simple.

So next time you go to type useMemo, pause and ask: Is this really needed? Or can I simplify with use() instead?