DEV Community

Cover image for Learn React useMemo the Right Way — Re-Renders, Optimization,Dependencies & useEffect
Kathirvel S
Kathirvel S

Posted on

Learn React useMemo the Right Way — Re-Renders, Optimization,Dependencies & useEffect

Welcome back to “Let’s Master React Hooks Together” — the series where we learn React hooks step by step in the most beginner-friendly way possible.

So far in this series, we’ve already explored hooks like useState and useEffect, and by now you’ve probably started noticing one important thing about React:

React components re-render a lot.

And honestly, that’s completely normal. React is designed that way.

But once applications start growing, re-rendering can sometimes create performance problems — especially when expensive calculations run again and again unnecessarily.

That’s exactly where today’s hook enters the picture:

useMemo()
Enter fullscreen mode Exit fullscreen mode

At first glance, useMemo looks confusing. Many beginners memorize the syntax without actually understanding:

  • what problem it solves,
  • why it exists,
  • and how it connects with useEffect.

So in this 8th episode, we’re going to slow things down and understand useMemo properly from the ground up.

We’ll learn:

  • the official definition,
  • beginner-friendly explanation,
  • how memoization works,
  • how dependency arrays behave,
  • how useEffect connects with useMemo,
  • why useEffect does not return values,
  • and how useMemo actually returns optimized values.

And just like the previous episodes in this series, we’ll not only write code — we’ll also break down the execution flow step by step so you can understand what React is actually doing behind the scenes.

So let’s dive in and finally make useMemo feel simple.


Official Definition of useMemo

According to the React documentation:

useMemo is a React Hook that lets you cache the result of a calculation between re-renders.

This is the official explanation. But honestly, for beginners, this sentence alone is not very easy to understand.

So let’s simplify it.


Beginner Friendly Definition of useMemo

In simple words:

useMemo remembers a calculated value so React doesn’t calculate it again and again during every re-render.

Or even simpler:

useMemo helps React avoid unnecessary work.

That is the core idea.

React components re-render frequently. During those renders, calculations also run again. If some calculation is expensive, repeatedly running it can slow down the application. useMemo helps optimize that.


Before Learning useMemo, Understand Re-Rendering

To properly understand useMemo, you first need to understand one very important concept in React:

React re-renders components very often.

Whenever:

  • state changes,
  • props change,
  • or parent components re-render,

React runs the component function again.

Look at this example:

import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);

  console.log("Component Rendered");

  return (
    <div>
      <h1>{count}</h1>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

At first, this code looks very small and harmless. But internally, React is doing more than beginners realize.


Step-by-Step Flow of the Code

When the component loads for the first time, React starts executing:

function App()
Enter fullscreen mode Exit fullscreen mode

Inside the component, React creates state using:

const [count, setCount] = useState(0);
Enter fullscreen mode Exit fullscreen mode

Now:

  • count becomes 0
  • setCount becomes the function that updates state

After that, this line runs:

console.log("Component Rendered");
Enter fullscreen mode Exit fullscreen mode

Then React returns the JSX and displays the UI.

When the button is clicked:

setCount(count + 1)
Enter fullscreen mode Exit fullscreen mode

React updates the state.

And this is the important part:

React re-renders the entire component again.

That means:

  • the component function runs again,
  • variables recreate,
  • functions recreate,
  • calculations rerun,
  • console logs rerun.

This behavior is exactly why hooks like useMemo exist.


Why useMemo Exists

Most of the time, re-rendering is completely fine. React is fast.

But imagine your component contains:

  • huge filtering logic,
  • expensive calculations,
  • sorting large arrays,
  • heavy data processing.

Now every re-render becomes expensive.

Even unrelated state changes can trigger those calculations again.

That creates unnecessary work.

To solve this optimization problem, React introduced:

useMemo()
Enter fullscreen mode Exit fullscreen mode

Its job is simple:

  • remember old calculated results,
  • and reuse them if dependencies did not change.

Official Syntax of useMemo

const memoizedValue = useMemo(() => {
  return expensiveCalculation();
}, [dependencies]);
Enter fullscreen mode Exit fullscreen mode

At first glance, this syntax may look scary. But it becomes simple once we break it down carefully.


Understanding the Syntax Step by Step

The first part is the function:

() => {
  return expensiveCalculation();
}
Enter fullscreen mode Exit fullscreen mode

This contains the calculation React should remember.

And notice something important here:

return expensiveCalculation();
Enter fullscreen mode Exit fullscreen mode

useMemo RETURNS a value.

This is one of the biggest differences between useMemo and useEffect.

Then comes the dependency array:

[dependencies]
Enter fullscreen mode Exit fullscreen mode

This tells React:

“Only rerun this calculation if these values change.”

This dependency system is exactly why beginners often connect useMemo with useEffect.

Because useEffect also uses dependency arrays.


Official Definition of useEffect

According to React:

useEffect lets you synchronize a component with external systems.

That sounds complicated too.

So let’s simplify it.


Beginner Friendly Definition of useEffect

In simple words:

useEffect runs some code after rendering.

Usually developers use it for:

  • API calls,
  • timers,
  • localStorage,
  • subscriptions,
  • DOM updates.

So while useMemo focuses on remembering values, useEffect focuses on running actions.


Very Important Difference

useEffect Does NOT Return Values

This is one of the most important beginner understandings.

Look at this:

const value = useMemo(() => {
  return 10;
}, []);
Enter fullscreen mode Exit fullscreen mode

Here:

useMemo
Enter fullscreen mode Exit fullscreen mode

returns a value.

That returned value gets stored inside:

value
Enter fullscreen mode Exit fullscreen mode

Now compare it with:

useEffect(() => {
  console.log("Hello");
}, []);
Enter fullscreen mode Exit fullscreen mode

This does NOT return a UI value.

useEffect simply runs some code after rendering.

So:

Hook Returns Value?
useMemo Yes
useEffect No

This is a very important connectivity proof between both hooks.

One stores values.
The other performs actions.


Important Connection Between useMemo and useEffect

This is where many beginners get confused.

Both hooks use dependency arrays.

Example:

useEffect(() => {
  console.log("Effect running");
}, [count]);
Enter fullscreen mode Exit fullscreen mode

and

useMemo(() => {
  return count * 2;
}, [count]);
Enter fullscreen mode Exit fullscreen mode

Both are watching:

[count]
Enter fullscreen mode Exit fullscreen mode

Whenever count changes:

  • useEffect reruns,
  • useMemo recalculates.

That’s why they feel similar.

But internally, their purpose is different.


Difference Between useMemo and useEffect

The easiest way to understand them is this:

Hook Purpose
useMemo Remember calculated values
useEffect Run side effects/actions

You can also remember it like this:

useMemo

“Remember this value.”

useEffect

“Run this action.”

This one-line understanding helps a lot.


Example Without useMemo

Now let’s see the real problem.

import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  const [dark, setDark] = useState(false);

  function slowFunction(num) {
    console.log("Heavy Calculation Running...");

    for (let i = 0; i < 1000000000; i++) {}

    return num * 2;
  }

  const doubledNumber = slowFunction(count);

  return (
    <div>
      <h1>{doubledNumber}</h1>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>

      <button onClick={() => setDark(!dark)}>
        Toggle Theme
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

At first, this code may look fine. But there’s an important issue hidden inside it.


What Happens Internally Here?

When the component renders:

  • slowFunction(count) runs,
  • heavy calculation happens,
  • React displays the result.

Now suppose the user clicks:

Toggle Theme
Enter fullscreen mode Exit fullscreen mode

This updates:

dark
Enter fullscreen mode Exit fullscreen mode

state.

Even though:

  • count did not change,
  • the heavy calculation still runs again.

Why?

Because React re-rendered the component, and everything inside the component executed again.

That is unnecessary work.

This is exactly where useMemo helps.


Fixing It with useMemo

import { useMemo, useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  const [dark, setDark] = useState(false);

  function slowFunction(num) {
    console.log("Heavy Calculation Running...");

    for (let i = 0; i < 1000000000; i++) {}

    return num * 2;
  }

  const doubledNumber = useMemo(() => {
    return slowFunction(count);
  }, [count]);

  return (
    <div>
      <h1>{doubledNumber}</h1>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>

      <button onClick={() => setDark(!dark)}>
        Toggle Theme
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now the behavior changes.


Flow of This Code

When the component renders for the first time:

  • useMemo runs,
  • slowFunction(count) executes,
  • React stores the result,
  • React also stores the dependency:
[count]
Enter fullscreen mode Exit fullscreen mode

Now suppose theme changes.

React re-renders the component again.

But before recalculating, React compares old dependencies with new dependencies.

If:

count
Enter fullscreen mode Exit fullscreen mode

did not change,
React skips the expensive calculation and reuses the old stored value.

This is called:

memoization
Enter fullscreen mode Exit fullscreen mode

And that’s where the name useMemo comes from.


Combining useMemo with useEffect

Now let’s connect both hooks together.

import { useEffect, useMemo, useState } from "react";

function App() {
  const [number, setNumber] = useState(1);

  const squared = useMemo(() => {
    console.log("Calculating...");
    return number * number;
  }, [number]);

  useEffect(() => {
    console.log("Squared value changed:", squared);
  }, [squared]);

  return (
    <div>
      <h1>{squared}</h1>

      <button onClick={() => setNumber(number + 1)}>
        Increase
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This example is the best proof of connectivity between:

  • useMemo
  • and useEffect

Now get in to this flow

When the component loads:

  • number state initializes,
  • useMemo calculates:
number * number
Enter fullscreen mode Exit fullscreen mode
  • React stores the returned value inside:
squared
Enter fullscreen mode Exit fullscreen mode

This proves:

useMemo
Enter fullscreen mode Exit fullscreen mode

returns a value.

Now React renders the UI.

After rendering finishes:

useEffect
Enter fullscreen mode Exit fullscreen mode

runs.

Notice:

  • useEffect did not create a value,
  • it only performed an action:
console.log()
Enter fullscreen mode Exit fullscreen mode

This proves:

useEffect
Enter fullscreen mode Exit fullscreen mode

does NOT return UI values.

Now suppose the button is clicked.

setNumber(number + 1)
Enter fullscreen mode Exit fullscreen mode

updates state.

React re-renders again.

Then:

  • useMemo checks [number]
  • dependency changed,
  • calculation reruns,
  • new squared value gets returned.

Now:

useEffect
Enter fullscreen mode Exit fullscreen mode

detects that:

[squared]
Enter fullscreen mode Exit fullscreen mode

changed.

So the effect runs again.

This is the real relationship:

  • useMemo creates optimized returned values
  • useEffect reacts when those values change

Once beginners understand this execution flow, both hooks become much easier.


Common Beginner Mistakes

One of the biggest beginner mistakes is using useMemo everywhere.

Example:

const value = useMemo(() => {
  return count + 1;
}, [count]);
Enter fullscreen mode Exit fullscreen mode

This is unnecessary because:

count + 1
Enter fullscreen mode Exit fullscreen mode

is already extremely fast.

useMemo itself also has a small cost.

So optimization should only happen when calculations are actually expensive.

Another common mistake is incorrect dependencies:

useMemo(() => {
  return count * 2;
}, []);
Enter fullscreen mode Exit fullscreen mode

Now the value never updates because dependencies are empty.

The same dependency problems also happen in useEffect.

So dependency arrays are very important in both hooks.


Final Thoughts

For beginners, useMemo feels confusing because it introduces optimization concepts early.

But the core idea is actually simple.

React components re-render often. During those renders, calculations also rerun. If those calculations are expensive, useMemo helps React remember previous results and avoid unnecessary work.

At the same time:

  • useMemo returns optimized values,
  • while useEffect performs actions after rendering.

So together:

  • useMemo stores optimized calculated values,
  • useEffect reacts when values change.

And that’s a wrap for Episode 8 of “Let’s Master React Hooks Together.”

If useMemo felt confusing before, hopefully now it feels much more practical and less scary.

In the next episodes, we’ll continue exploring more React hooks, deeper concepts, and real-world React patterns step by step in the same beginner-friendly style.

Until then:

  • experiment with these examples,
  • change dependencies,
  • observe re-renders,
  • and keep building projects.

Because the more you practice React hooks, the more natural they become.

See you in Episode 9 of “Let’s Master React Hooks Together.”

Top comments (0)