useMemo
and useCallback
are React hooks for memoization. Think of memoization as caching a value so that it does not need to be recalculated. This improves performance. The main difference between useMemo
and useCallback
is that useCallback
returns a memoized function (which just really means a 'cached function') and useMemo
returns a memoized value. Let's go through these hooks together.
Let's start with useMemo
Using useMemo
One reason to use useMemo
is to prevent an expensive function from re-rendering unless one of its dependencies update.
Problem
In this example, we have an expensive function that runs on every render.
When changing the count or adding a todo, you will notice a delay in execution.
import { useState } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = expensiveCalculation(count);
const increment = () => {
setCount((c) => c + 1);
};
const addTodo = () => {
setTodos((t) => [...t, "New Todo"]);
};
return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
<button onClick={addTodo}>Add Todo</button>
</div>
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
<h2>Expensive Calculation</h2>
{calculation}
</div>
</div>
);
};
const expensiveCalculation = (num) => {
console.log("Calculating...");
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
};
ReactDOM.render(<App />, document.getElementById('root'));
Solution
To fix this performance issue, we have to find a way to prevent the rerender of the expensive function. To do this we'll have to memoize the expensive function. This is done by wrapping the expensive function call with useMemo
.
The useMemo
Hook accepts a second parameter to declare dependencies. The expensive function will only run when its dependencies have changed.
In the following example, the expensive function will only run when count is changed and not when todo's are added.
import { useState, useMemo } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = useMemo(() => expensiveCalculation(count), [count]);
const increment = () => {
setCount((c) => c + 1);
};
const addTodo = () => {
setTodos((t) => [...t, "New Todo"]);
};
return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
<button onClick={addTodo}>Add Todo</button>
</div>
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
<h2>Expensive Calculation</h2>
{calculation}
</div>
</div>
);
};
const expensiveCalculation = (num) => {
console.log("Calculating...");
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
};
ReactDOM.render(<App />, document.getElementById('root'));
Let's look at useCallback!!!
Using useCallback
The main difference between useMemo and useCallback is that useCallback returns a memoized function (which just really means a 'cached function') and useMemo returns a memoized value.
( for the code example for useCallback
, we'll use the exact example used for the useMemo
but the expensive function would be in another component called ExpensiveCalc.js
.)
import { useState } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const increment = () => {
setCount((c) => c + 1);
};
const expensiveCalculation = useCallback((num) => {
console.log("Calculating...");
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
}, [count]);
return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
<button onClick={addTodo}>Add Todo</button>
</div>
<hr />
<div>
<ExpensiveCalc count={count} increment={increment}/>
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
ExpensiveCalc.js
import { memo } from "react";
const ExpensiveCalc = ({ count, increment }) => {
console.log("child render");
return (
<>
<h2>Count</h2>
Count: {count}
<button onClick={increment}>+</button>
</>
);
};
export default memo(ExpensiveCalc); //memo will cause React to skip rendering a component if its props have not changed.
Top comments (3)
Hello!
If you'd like, you can add syntax highlighting (colors that make code easier to read) to your code block like the following example.
This should make it a bit easier to understand your code. 😎
In order to use this in your code blocks, you must designate the coding language you are using directly after the first three back-ticks. So in the example above, you'd write three back-ticks js (no space between the two), put the code beneath (e.g.
console.log('Hello world!');
), then three back-ticks beneath that to close the code.Here's an image that shows how it's written!
thanks so much, was actually looking on how to highlight the code snippets.
Thanks bro!