Introduction
Here, we will talk about some hooks that we use to optimize the performance of the process of some expensive calculations, or we can optimize project performance. These hooks let you cache a function definition between re-renders or cache some values from expensive calculations, which calculate on every render and its effects on the performance of the project.
Topics we will cover in this article.
- use callback
- useMemo
- Memo
use callback
use callback is a hook that is used to freeze functions that calculate very expensive calculations, and it calculates on every render, so this is not what we want because it affects on performance of the project or component. So, to fix this problem, we can use the useCallback hook to freeze that function .it allows us to isolate resource-intensive functions so that they will not automatically run on every render.
useCallback only runs when one of its dependencies updates.
Syntax
use callback(function dependencies);
const updatedFunction = useCallback(
(args) => {
// action goes in here
},
[dependencies]
);
When to use the use callback hook?
- When you need to pass a function as props to a child component.
- If you have a function that is expensive to compute and you need to call it in multiple places.
- When dealing with functional components.
- When you are working with a function that relies on external data or state.
These are uses of use callback.
useMemo
useMomo is a hook used for memoized value no function. The useMemo runs only when one of its dependencies updates.
We use the useMemo hook to prevent expensive, resource-intensive functions from needlessly running.
Here we will take one example with poor performance and better performance.
With poor performance
import { useState } from "react";
import ReactDOM from "react-dom/client";
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;
};
If we notice in the above example that when it runs it updates new todo after a little bit of time this is just because of the expensive calculation function that runs on every render needlessly. so to fix this issue we can use useMemo.
Better performance example
import { useState, useMemo } from "react";
import ReactDOM from "react-dom/client";
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;
};
Now notice in the above example that when we click on the add todo button it does not take a little bit of time to update todo because we added an expensive calculation function in useMemo and made it isolate and it runs only when its count value is updated. So in such a way, we can useMemo.
Memo
This is a higher-order component that lets you memoize a functional component. Memoization is an optimization technique that allows you to cache the result of a function call and return the cached result when the same input is passed to a function again. This can be useful for some critical components' performance that are expensive to render.
In React, we implement memorization via React. memo(). this serves as a wrapper for a component and returns a memoized output that prevents unnecessary re-rendering.
There are two ways to use it.
The first one is given below.
import { useState, useMemo } from "react";
import ReactDOM from "react-dom/client";
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;
};
The second one is.
const newComponent = (props) => {
// render with props
}
export default React.memo(newComponent);
The syntax above denotes that we can memoize a component by simply passing it as an argument to React.memo and exporting the result.
The difference between use callback and useMemo
useCallback and useMemo are similar, but our key difference is that useMemo does not memoize the whole function or component. It only memoizes one value that is returned by the function, but useCallback is used to memoize the whole function to memoize freeze till one of the dependencies updates.
Conclusion
In this article, we explored the key concepts related to useCallback, useMemo, and Memo, including their purpose, usage, and benefits. We also compared useCallback with other related hooks like useMemo and highlighted their differences and appropriate use cases.