← Back to list

React.memo VS useCallback VS useMemo - React Re-rendering Explained Clearly

Master React performance optimization by understanding how React.memo, useCallback, and useMemo solve different rendering problems.

Anudeepthi kolagani · 2026-05-20 03:36 · 0 claps · 6.5 min read
#react #react-hook #memoization #usecallback-hook #usememo-hook
Open on Medium ↗
Wiki topics: 🌐 · Web Development

𝐑𝐞𝐚𝐜𝐭.𝐦𝐞𝐦𝐨 𝗩𝗦 𝐮𝐬𝐞𝐂𝐚𝐥𝐥𝐛𝐚𝐜𝐤 𝗩𝗦 𝐮𝐬𝐞𝐌𝐞𝐦𝐨— 𝐑𝐞𝐚𝐜𝐭 𝐑𝐞-𝐫𝐞𝐧𝐝𝐞𝐫𝐢𝐧𝐠 𝐄𝐱𝐩𝐥𝐚𝐢𝐧𝐞𝐝 𝐂𝐥𝐞𝐚𝐫𝐥𝐲

Ever opened React Dev Tools and wondered:

“Why this component re-rendering again ?”

Even when nothing visibly changed, your components keep rendering repeatedly.

Sometimes that’s completely fine.

But when expensive calculations, API heavy components, large lists, or complex UI trees are involved, unnecessary re-renders can slowly make your React app feels sluggish.

This is exactly where:

React.memo

useCallback

useMemo

come into the picture.

Lets see how all three solve different parts of the same rendering problem.

Understanding Re-renders

Before exploring the above mentioned hooks, let’s first understand:

Why does React re-render components?

Whenever a component’s state changes, React re-executes the component function.

import Child from "./Child";

const Parent = () => {
  console.log("Parent component Rendered");

  return (
    <div className="w-6/12 flex flex-col text-center border border-gray-500 bg-blue-50 p-4 m-4 justify-center items-center gap-2">
      <h1 className="text-3xl">Parent Component</h1>

      {/* Passing props to Child component */}
      <Child
        user={{
          name: "Anu",
          email: "anu@example.com",
        }}
      />
    </div>
  );
};

export default Parent;
const Child = ({ user }) => {
  console.log("Child component Rendered");

  return (
    <div className="child mt-10">
      <h1 className="text-2xl">Child Component</h1>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
};

export default Child;

Whenever the parent component re-renders:

  • The entire ‘Parent’ function runs again
  • Variables are re-created
  • Functions are re-created
  • Child component also re-render by default

But wait..

Why does the Child component re-render when only the Parent component re-renders?

Because

  • React follows a top-down rendering model.
  • So when a parent component re-renders, React re-executes all child components by default.
  • React does not automatically know whether the child output will remain the same.
  • So unless optimization is applied, child components also re-render.

Even if there is no change in user, the Child component still re-renders.

State Update ↓ Parent Re-render ↓ Child Re-render ↓ Functions & Objects Recreated

For small components, this usually isn’t a problem.

But imagine:

  • Heavy calculations
  • large tables
  • complex charts
  • Expensive filtering/sorting
  • API-heavy UI

running again on every unnecessary re-render. That’s when performance issues begin.

Wouldn’t it be great if React could check whether props changed and skip unnecessary re-renders?

That’s exactly what React.memo helps with.

React.memo → Memoizing components

React.memo helps prevent unnecessary component re-renders.

It tell React:

“If props didn’t change, reuse the previous rendered result.”

import React from "react";

// Wrapping component inside React.memo
const Child = React.memo(({ user }) => {
  console.log("Child component Rendered");

  return (
    <div className="child mt-10">
      <h1 className="text-2xl">Child Component</h1>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
});

export default Child;
  • If props are unchanged → Child component will NOT re-render
  • If props changed → React re-renders the component

Now you may think, How React.memo know whether Props or changed or not?

This confused me initially.

React compares previous props with current props. It uses shallow comparison.

for every prop, React checks.

  • Primitive values like strings and numbers compare by value.
  • But objects, arrays, and functions compare by reference.

prevProp === nextProp

What Happens Behind The Scenes?

Internally, React stores previously memoized values and dependencies inside the Fiber tree.

On every render:

  • React checks dependency arrays
  • compares old dependencies with new dependencies
  • decides whether to reuse cached values/functions or create new ones

The Function Reference Problem

So far, we discussed how React.memo prevents unnecessary re-renders when props remain unchanged.

But now let’s look at a slightly different example.


import { useState } from "react";
import Child from "./Child";

const Parent = () => {
  const [theme, setTheme] = useState("dark");

  console.log("Parent component Rendered");

  // Without useCallback:
  // New function gets created on every render
  const handleLogin = () => {
    console.log("Logging in...");
  };

  return (
    <div className="w-6/12 flex flex-col text-center border border-gray-500 bg-blue-50 p-4 m-4 justify-center items-center gap-2">
      <h1 className="text-3xl">Parent Component</h1>

      <p>Current Theme: {theme}</p>

      <button
        className="w-40 border border-blue-500 rounded px-4 py-1"
        onClick={() => setTheme((prev) => (prev === "dark" ? "light" : "dark"))}
      >
        Change Theme
      </button>

      <Child onLogin={handleLogin} />
    </div>
  );
};

export default Parent;
import React from "react";

const Child = React.memo(({ onLogin }) => {
  console.log("Child component Rendered");

  return (
    <div className="mt-10">
      <h1 className="text-2xl">Child Component</h1>

      <button
        className="border border-green-500 px-4 py-1 rounded"
        onClick={onLogin}
      >
        Login
      </button>
    </div>
  );
});

export default Child;

When the above code is executed , you can see this in console

Now try changing the theme. and observe the console

You may be surprised to see that the Child component still re-renders even though we used React.memo.

Why?

Because functions create a NEW reference on every render.

Even though the function logic remains the same:

Old Function !== New Function

So React thinks the prop changed.

This is where useCallback comes into the picture.

useCallback → Memoizing Functions

Instead of returning a newly created function on every render, React reuses the previously cached function reference.

It returns the SAME FUNCTION UNTIL DEPENDENCIES CHANGE. React reuses the cached function reference.

 const handleLogin = useCallback(() => {
    console.log("Logging in...");
  }, []);

Now when the component re-renders:

  • React checks dependencies
  • If dependencies are unchanged →React reuses the same function reference
  • If dependencies changed → React creates and caches a new function

Render ↓ React checks dependencies ↓ Dependencies unchanged? ↓ YES → Reuse cached function NO → Create new function

One important thing I learned while diving deep into useCallback:

useCallback does NOT prevent function creation.

The component still creates a new function during rendering.

But React ignores the newly created function and returns the previously cached function reference instead.

That was a huge realization for me.

Why useCallback Is Commonly Used With React.memo

  • React.memo checks prop references
  • useCallback keeps function references stable

Together, they help prevent unnecessary child component re-renders.

Without useCallback:

onLogin !== previousOnLogin

With useCallback:

onLogin === previousOnLogin

And because the reference stays the same, React.memo can successfully skip re-rendering the child component.

useMemo → Memoizing Values

Now we know how to memoize:

  • components
  • functions

But what about expensive calculated values?

That’s where useMemo helps.

import { useMemo, useState } from "react";

const Parent = () => {
  const [count, setCount] = useState(0);
  const [theme, setTheme] = useState("dark");

  console.log("Parent Component Rendered");

  // Normal calculation without memoization
  // console.log("Calculating square...");
  // const squaredValue = count * count;

  //When count changes, squaredValue will be recalculated. 
  // If theme changes, squaredValue will not be recalculated because it is not a dependency.
  const squaredValue = useMemo(() => {
    console.log("Calculating square...");
    return count * count;
  }, [count]);

  return (
    <div className=" w-6/12 flex flex-col gap-4 items-center justify-center text-center border border-blue-500 rounded mt-10 mx-auto p-4">
      <p>Count: {count}</p>
      <p>Squared Value: {squaredValue}</p>

      <button
        className="border border-blue-500 px-4 py-1 rounded"
        onClick={() => setCount(count + 1)}
      >
        Increment Count
      </button>

      <button
        className="border border-blue-500 px-4 py-1 rounded"
        onClick={() => setTheme("light")}
      >
        Change Theme
      </button>
    </div>
  );
};

export default Parent;

Without useMemo:

const squaredValue = count * count;

the calculation runs on EVERY render.

Even when only the theme changes.

But with useMemo:

  • When count changes → recalculation happens
  • When theme changes → React reuses cached value

State Update ↓ Component Re-render ↓ React checks dependencies ↓ Dependencies changed? ↓ YES → Recalculate value NO → Reuse cached value

Important Insight About useMemo

One thing that became clear to me while learning useMemo:

useMemo does NOT prevent re-renders.

The component still re-renders.

useMemo only prevents unnecessary recalculation of expensive values.

That distinction is extremely important.

React.memo → memoizes component useCallback → memoizes function useMemo → memoizes value

When NOT To Use Memoization

This was another important lesson for me.

Memoization itself has a cost.

Overusing these hooks can:

  • increase complexity
  • reduce readability
  • create unnecessary optimization overhead

If your component is already fast, you probably don’t need optimization.

Optimization should solve a real performance problem — not imaginary ones.

Initially, I thought:

  • useMemo
  • useCallback
  • React.memo

were almost doing the same thing.

But after diving deeper into React rendering, I realized they solve completely different problems.

Understanding them is less about memorizing syntax, and more about understanding how React handles rendering and references internally.

And once you understand that, React optimization starts making much more sense.


메타데이터
post_id
fb487947d55b
slug
react-memo-vs-usecallback-vs-usememo-react-re-rendering-explained-clearly-fb487947d55b
url
https://medium.com/@AnudeepthiKolagani/react-memo-vs-usecallback-vs-usememo-react-re-rendering-explained-clearly-fb487947d55b
canonical_url
https://medium.com/@AnudeepthiKolagani/react-memo-vs-usecallback-vs-usememo-react-re-rendering-explained-clearly-fb487947d55b
author_url
https://medium.com/@AnudeepthiKolagani
status
ok
fetched_at
2026-08-19 11:02:14