Memo, useMemo and useCallback ftw
Memoization is a technique in React that optimizes the performance of the application by caching the results of expensive functions and reusing them when the inputs to those functions have not changed. Memoization is achieved in React through the use of the memo
Higher Order Component (HOC).
The memo
HOC is used to wrap a component and control its re-rendering. It does this by comparing the props passed to the component during rendering with the props passed during the previous render. If the props have not changed, the component is not re-rendered, and the cached result is used instead.
Here are some cases when you should use memo
in React:
1. Pure Functional Components
If your component is a pure functional component, meaning that it only accepts props and does not have any internal state, then you should use memo
to cache the result of rendering the component.
2. Expensive Rendering
If your component has an expensive rendering process, such as a large number of nested child components, you should use memo
to cache the result of rendering the component. This will reduce the number of times the component is re-rendered and improve the performance of your application.
3. Component with Static Props
If your component receives static props that do not change during the lifecycle of the component, you should use memo
to cache the result of rendering the component. This will prevent unnecessary re-renders and improve the performance of your application.
4. Avoiding Re-renders
If your component does not need to be re-rendered when certain props change, you can use memo
to prevent the re-rendering of the component. This is useful when you have a component with a large number of child components, and you want to optimize the performance of the application by reducing the number of re-renders.
In conclusion, memo
is a powerful tool in React that can significantly improve the performance of your application. You should use it when you have pure functional components, expensive rendering processes, static props, or when you want to avoid unnecessary re-renders.
Examples of using
Header component
import React, { memo } from 'react'
interface HeaderProps {
name: string
profileImageUrl: string
}
const Header = ({name, profileImageUrl}: HeaderProps) => {
return (
<div>
<label> {name} </label>
<img src={profileImageUrl} alt="Picture of a person" />
</div>
)
}
export default memo(Header)
Parent component
import React, { memo, useState } from 'react'
import Header from './Header'
export default function App(){
return (
<div>
<Header name="Yoshi" profileImageUrl="https://avatars.githubusercontent.com/u/9868584?v=4" />
</div>
)
}
Important alert
React does a shallow comparison of the props. It means, if you have primitive values like string, number, boolean or null, the memo will work well because react will compare by the value. When you pass an object or an array through the component as a prop, it will compare by the reference. So, when you pass an object as a parameter, react will ever create a new reference to the object even tough the new object has the same value of the previous object. So, you have to make like this:
import React, { memo } from 'react';
const MyComponent = ({params}) => {
return <div style={{ backgroundColor: params.color}} />
}
const MemoComponent = memo(MyComponent, (prevProps, nextProps) => {
return prevProps.params.color === nexProps.params.color //color is a primitive type
})
export default MemoComponent;
import React, {useState} from 'react';
import MemoComponent from './MemoComponent';
const ParentComponent = () => {
const [color, setColor] = useState("blue")
return (
<div>
<h1>My Parent Component</h1>
<MemoComponent params={{color}} />
<button onClick={() => setColor(color === "blue" ? "red": "blue")} />
</div>
);
};
export default ParentComponent;
In the end of the day
You don't need to compare the object atributes, just use useMemo
Here's an example of using useMemo in a React component:
import React, { useMemo, useState } from 'react';
const MyComponent = ({ items }) => {
const [searchTerm, setSearchTerm] = useState('');
// Memoize the filtered list of items
const filteredItems = useMemo(() => {
return items.filter(item => item.includes(searchTerm));
}, [items, searchTerm]);
return (
<div>
<input type="text" value={searchTerm} onChange={e => setSearchTerm(e.target.value)} />
<ul>
{filteredItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
};
export default MyComponent;
In this example, we have a component that receives a list of items as a prop. We use the useMemo
hook to memoize the filtered list of items based on the items
array and the searchTerm
state. This means that the filtered list will only be recalculated when either the items
array or the searchTerm
state changes.
Best practices using useMemo
Bad:
const sum = useMemo(() => number1 + number2, [number1, number2])
Sum is a primitive value (number). You should use like:
const sum = number1 + number2;
Same thing for strings
Bad:
const user = useMemo(() => `${name}, ${age}`, [name, age])
User is a primitive value (string). You should use like:
const user = `${name}, ${age}`
but, when we have cases like that, you should use memo to cache the operation
Good:
const sum = useMemo(() => numbers.reduce((a, v) => a + v, 0), [numbers])
Sum still being a primitive value but we don't know how big is the number
array. So the Array.reduce will run just when number
changes, not in all renders, improving the app performance. :)
You can maintain the object reference identity just doing:
const sum = useMemo(() => {
first,
last,
full: `${first} ${last}`
}, [first, last])
About useCallback
Like useMemo, you can useCallback to maintain function references
Here is an example of using useCallback
in a React component:
import React, { useState, useCallback } from 'react';
const MyComponent = () => {
const [count, setCount] = useState(0);
// Memoize the increment function
const increment = useCallback(() => {
setCount(currentCount => currentCount + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
export default MyComponent;
In this example, we have a component that displays a count and a button to increment the count. We use the useCallback
hook to memoize the increment
function based on the setCount
function. This means that the increment
function will only be recreated when the setCount
function changes.
Best practices using useCallback
You should use useCallback
when you need to memoize a function that is passed down to child components as a prop. This will prevent the child components from needlessly re-rendering when the function reference changes.
Bad:
const handleClick = () => {
// do something
};
return <MyButton onClick={handleClick} />;
In this example, the handleClick
function is recreated on every render. This means that MyButton
will be needlessly re-rendered every time the parent component re-renders.
Good:
const handleClick = useCallback(() => {
// do something
}, []);
return <MyButton onClick={handleClick} />;
In this example, the handleClick
function is memoized using useCallback
. This means that MyButton
will only re-render if the props passed to it change, rather than every time the parent component re-renders.
When to use useMemo vs useCallback
You should use useMemo
when you need to memoize a value that is expensive to calculate. You should use useCallback
when you need to memoize a function that is passed down to child components as a prop.
In general, you should use useMemo
for values and useCallback
for functions.
Now you don't have problems anymore. Hugs.
Top comments (0)