Table of contents
- Let’s start with the useCallback hook
- useCallback hook
- Let’s walk through an example to see when this is useful
- Let’s walk through an example to see when this is useful
- Skipping expensive recalculations
- Skipping re-rendering of components
- Memoizing a function
- Let’s see the diffrence between useCallback and useMemo hook
Today we’ll look at the useCallback
and useMemo hooks in depth and compare them.
When discussing performance optimization in React, useCallback
and use Memo hook come into the picture. These hooks are both used to optimize the app.
Let’s start with the useCallback hook
useCallback
hook
useCallback
caches a function between re-renders until its dependencies change.
You need to pass two things to useCallback
:
A function definition that you want to cache between re-renders.
A list of dependencies including every value within your component that’s used inside your function.
syntax
const handleSubmit = useCallback(() => {
}, [dependencies]);
import { useCallback } from 'react';
function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useCallback((orderDetails) => {
console.log(orderDetails)
}, [productId, referrer]);
Here, we defined the handleSubmit function, which will be created once the app renders. When the component ProductPage re-renders, useCallback checks to see if any of its dependencies have changed and if so, it will re-create the function; otherwise, it will not.
Let’s walk through an example to see when this is useful
import { useCallback } from 'react';
import Shipping from './Shipping'
function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useCallback((orderDetails) => {
console.log(orderDetails)
}, [productId, referrer]);
return (
<div className={theme}>
<Shipping onSubmit={handleSubmit} />
</div>
);
Here, we are passing handleSubmit as a prop to the Shipping component, and each time ProductPage component re-renders it’ll re-render all its children but handleSubmit
will not re-created because it’s dependencies didn’t change.
you can tell Shipping
to skip re-rendering when its props are the same as on last render by wrapping it in memo(we'll learn it in depth)
After wrapping shipping function inside useMemo
hook, we are saying to the react that only re-render this component if any of its props change.
import { memo } from "react";
const Shipping = memo(function Shipping({ onSubmit }) {
console.log("shipping");
return (
<div>
<h1 onClick={() => onSubmit()}>Hello I'm SHIPPING</h1>
</div>
);
});
export default Shipping;
By wrapping handleSubmit
in useCallback
, you ensure that it’s the same function between the re-renders (until dependencies change). You don’t have to wrap a function in useCallback
unless you do it for some specific reason. In this example, the reason is that you pass it to a component wrapped in memo
, and this lets it skip re-rendering.
Should you add useCallback everywhere?
You pass it as a prop to a component wrapped in
memo
. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only when dependencies aren’t the same.The function you’re passing is later used as a dependency of some Hook. For example, another function wrapped in
useCallback
depends on it, or you depend on this function fromuseEffect.
useMemo Hook
useMemo
is a React Hook that lets you cache the result of a calculation between re-renders.const cachedValue = useMemo(calculateValue, dependencies)
Let’s walk through an example to see when this is useful
Skipping expensive recalculations
To cache a calculation between re-renders, wrap it in a
useMemo
call at the top level of your component:useMemo
caches a calculation result between re-renders until its dependencies change.You need to pass two things to
useMemo
:A calculation function that takes no arguments, like
() =>
, and returns what you wanted to calculate.A list of dependencies including every value within your component that’s used inside your calculation.
import { useMemo } from 'react'; function TodoList({ todos, tab, theme }) { const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); // ... }
On the initial render, the value you’ll get from
useMemo
will be the result of calling your calculation.on every render
React
will compare the dependencies ofuseMemo
hook . If none of the dependencies have changed (compared withObject.is
),useMemo
will return the value you already calculated before. Otherwise, React will re-run your calculation and return the new value.Usually, this isn’t a problem because most calculations are very fast. However, if you’re filtering or transforming a large array, or doing some expensive computation, you might want to skip doing it again if data hasn’t changed. If both
todos
andtab
are the same as they were during the last render, wrapping the calculation inuseMemo
like earlier lets you reusevisibleTodos
you’ve already calculated before. This type of caching is called memoization.
Should you add useMemo everywhere?
Optimizing with useMemo
is only valuable in a few cases:
- The calculation you’re putting in
useMemo
is noticeably slow, and its dependencies rarely change.
2. You pass it as a prop to a component wrapped in memo
. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only when dependencies aren’t the same.
3. The value you’re passing is later used as a dependency of some Hook. For example, maybe another useMemo
calculation value depends on it. Or maybe you are depending on this value from useEffect.
Skipping re-rendering of components
as we done before in shipping component.
another way of wrapping a component in useMemo
function Shipping({ onSubmit }) {
console.log("shipping");
return (
<div>
<h1 onClick={() => onSubmit()}>Hello I'm SHIPPING</h1>
</div>
)};
import { useCallback } from 'react';
import Shipping from './Shipping'
function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useCallback((orderDetails) => {
console.log(orderDetails)
}, [productId, referrer]);
const memoShipping = useMemo(() => <Shipping onSubmit={handleSubmit} />, [handleSubmit]);
return (
<div className={theme}>
{memoShipping}
</div>
);
Memoizing a function
import { useCallback } from 'react';
import Shipping from './Shipping'
function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useMemo(() => {
return (orderDetails) => {
console.log(orderDetails)
};
}, [productId, referrer]);
const memoShipping = useMemo(() => <Shipping onSubmit={handleSubmit} />, [handleSubmit]);
return (
<div className={theme}>
{memoShipping}
</div>
);
To memoize a function with useMemo
, your calculation function would have to return another function.
Let’s see the diffrence between useCallback and useMemo hook
Wrap your functions into useCallback
instead of useMemo
to avoid having to write an extra nested function
//USE MEMO HOOK
const handleSubmit = useMemo(() => {
return (orderDetails) => {
console.log(orderDetails)
};
}, [productId, referrer]);
//USECALLBACK HOOK
const handleSubmit = useCallback((orderDetails) => {
console.log(orderDetails)
}, [productId, referrer]);
The two examples above are completely equivalent. The only benefit to useCallback
is that it lets you avoid writing an extra nested function inside. It doesn’t do anything else. Read more about useCallback
.
Hope you like it 🤗
Happy coding!
want to give suggestions:-
Email me at nidhisharma639593@gmail.com