This content originally appeared on DEV Community and was authored by Paul-Simon Emechebe
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.
This content originally appeared on DEV Community and was authored by Paul-Simon Emechebe
Paul-Simon Emechebe | Sciencx (2022-07-04T03:10:38+00:00) Supercharging your React projects with Memoization. Retrieved from https://www.scien.cx/2022/07/04/supercharging-your-react-projects-with-memoization/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.