I’m sure most of you have used React.memo
, useMemo
or useCallback
before, but have you ever wondered how it works?
First, let’s start by explaining why we need memoization in the first place.
Why do we need memoization?
Comparing two objects in JavaScript is not as easy as comparing two numbers or two strings. For example, if we have two objects a
and b
with the same properties and values, a === b
will return false
. This is because a
and b
are two different objects in memory.
const a = { id: 1 };
const b = { id: 1 };
a === b; // will always be false
To compare two objects, we need to make sure that the reference in b is exactly the same as the reference in a.
This exact behavior causes issues sometimes in React. Let’s take a look at the following example:
function ChatRoom() {
function createConnection() {
// ...
}
useEffect(() => {
createConnection();
}, [createConnection]);
// ...
}
Each time the ChatRoom
component renders, a new createConnection
is created. This means that the useEffect
hook will run every time the component renders, even if the createConnection
function is the same.
And this is where memoization comes in handy. It allows us to cache a value between renders.
useMemo & useCallback
In the previous example, we want to cache the createConnection
function between renders. To do so, we can use the useCallback
hook.
function ChatRoom() {
const createConnection = useCallback(() => {
// no dependencies, reference won't change between re-renders
}, []);
useEffect(() => {
createConnection();
}, [createConnection]);
// ...
}
useCallback
will return a memoized version of the createConnection
function. This means that the createConnection
function will only be created once, and the same function will be used in the useEffect
hook.
If we needed to cache a value that is not a function, we would use the useMemo
hook instead.
function ChatRoom() {
const connection = useMemo(() => {
return createConnection();
}, []);
useEffect(() => {
connection();
}, [connection]);
// ...
}
Even though seems like a quick and easy solution, it’s not always the best one. In fact, memoization can cause more harm than good if not used properly.
In the case of useMemo
, we need to make sure that our computation is expensive enough to justify the overhead of memoization, also if our component never re-renders, memoization is useless, and we’re just wasting memory.
React.memo
React.memo
is a higher-order component that will return a memoized version of a component. It will only re-render the component if its props have changed.
import { memo } from "react";
const SomeComponent = memo(function SomeComponent(props) {
// ...
});
Again, in the case of non-primitive props, React.memo
will compare the references of the props. This means that if we pass a new object as a prop, the component will re-render even if the properties and values are the same.
To avoid this, we can use the useMemo
and useCallback
hooks to memoize the props.
import { memo, useMemo } from "react";
const SomeComponent = memo(function SomeComponent(props) {
// ...
});
const App = () => {
const props = useMemo(() => {
return {
id: 1,
name: "Siham",
};
}, []);
return <SomeComponent props={props} />;
};
The problem with this approach is how fragile it is. If we spread the props in the SomeComponent
component, the component will re-render even if the props haven’t changed.
import { memo, useMemo } from "react";
const SomeComponent = memo(function SomeComponent(props) {
// ...
});
const App = () => {
const props = useMemo(() => {
return {
id: 1,
name: "Siham",
};
}, []);
return <SomeComponent {...props} />;
};
There are many other cases where memoization can be broken, There is no easy way to solve this problem. The best solution is to avoid passing non-primitive props to the SomeComponent
component.
Conclusion
Memoization is a powerful technique that can help us improve the performance of our React applications. However, it is also easy to misuse. Therefore, we need to be careful when using it, and opt for a different solution if possible.
If you have any suggestions or questions, feel free to reach out to me on Twitter or LinkedIn.
P.S. this post is inspired by the book, but it’s not a summary of it. I’m just sharing what I learned from it. If you want to learn more about React, I highly recommend you to read it.