Salam and hello, folks!
Continuing the React Hook series, now I am going to talk about another 2 hooks, which are useLayoutEffect
and useMemo
. To the topic, we go!
But before that, I want to mention that these two are a rare hook, where you will only use them when there is a particular use case (even though I didn't encounter one yet). But don't worry, maybe you found this hook in your existing project and want to understand how these two hooks work. Well, I got you covered!
Let's start, shall we?
In this article, these are the topics covered:
useLayoutEffect
- Yet another lifecycle hook!- The difference between
useEffect
anduseLayoutEffect
, and why it matters useMemo
- The memoization hook- When to not use
useMemo
- Conclusion
useLayoutEffect
- Yet another lifecycle hook!
In the previous article in this series, I explained how useEffect
is one of the most used hook works. To simply put it a way to understand, it covers componentDidMount
and componentWillUnmount
. Well, useLayoutEffect
does a lot of the same thing as useEffect
, and in fact, the way you write useLayoutEffect
is the same!
useLayoutEffect(() => {
// do your `componentDidMount` thing here
return () => {
// do your `componentWillUnmount` here
};
}, [<all_dependencies_here>]);
useEffect(() => {
// do your `componentDidMount` here
return () => {
// your `componentWillUnmount` here
};
}, [<all_dependencies_here>]);
Welp, two hooks with quite similar names, with the same way to write them. What is the catch?
The difference between useEffect
and useLayoutEffect
, and why it matters
However, what difference does it make compared to useEffect
then?
Well, need to know a key difference of "when" both of these hooks run. useEffect
will run right after if there are any changes to any of the given dependencies to the hook, while useLayoutEffect
will run after each change to the "layout", which means if there is a change to the DOM (DOM mutations). This includes if the change involves ref as well.
Not to be mistaken, you indeed supply the same array of dependencies to useLayoutEffect
as you supplied to useEffect
, but it will run after the DOM change, and if any of the dependency changes. Unlike useEffect
which will run right after one of the dependencies changes.
So, when to use useEffect
and when to use useLayoutEffect
? Well, since useEffect
is triggered as the dependencies change, useEffect
is the hook you will use most of the time. useEffect
is DOM independent, which means that DOM doesn't affect the behaviour of useEffect
. Simply, useEffect
is to monitor state changes.
useLayoutEffect
triggers when there is a DOM mutation, so you can utilise that if you need to do some DOM-related activities, such as measuring performance, or detect DOM changes such as scroll position.
useMemo
- The memoization hook
The fourth hook in this series is useMemo
. This is for memoization. So what is memoization?
In programming, memoization is an optimization technique that makes applications more efficient and hence faster. It does this by storing computation results in a cache and retrieving that same information from the cache the next time it's needed instead of computing it again. - Germán Cocca, freeCodeCamp
Memoization is an optimization technique, so your app becomes faster by utilising caches. Just imagine, you are calculating the value of infinity, so you don't want to run it every time the component rerenders, right?
const [x, setX] = useState(0);
const valueOfInfinity = () => calculationOfInfinity(x);
Just imagine, this function will run EVERY TIME component rerenders 🥶
But first, let's see how useMemo is written, shall we?
useMemo(() => {}, [array_of_deps]);
// example
const valueOfInfinity = useMemo(() => calculationOfInfinity(x), [x]);
The first part of the useMemo
hook is the function you want to run. It could be an expensive function or something you want to keep a cache of the dependency. The expensive function here means that this requires a lot of resources to run the function.
The second part of the hook is the array of dependencies. And yes, it behaves similar to useEffect
, where it will only run the function when one of the dependency change value.
Let's say we consider the example above. Just imagine the formula to calculate infinity is extremely complex, and it will surely consume a lot of resources each time the function runs, right? And adding to that, it depends on x
, which possibly changes, since it is a state.
When you trigger useState
, it will trigger the rerenders. When that happens, the function will run each time, even though the state value is unchanged. You could even trigger the useState
to value "3" even though the value is already "3". But since setState
is triggered, the component will rerender anyway.
We don't want that to happen. If the value of dependency is unchanged, we want to keep it that way and not trigger the function. So useMemo
will hold the value of dependency and will observe the changes, so if the value is the same as the previous value, it won't run the function. So, even though we set x
to 3, even though x
is already equal to 3, the function won't run. Neat, right?
This can also be used to avoid children's rerenders too. Example:
const TheButton = useMemo(() => <button>This button</button>, []);
// In your return
return () => (
<TheButton />
);
In this example above, since you didn't add any dependency to the <button>
component, it will only run once. Even though your parent component rerenders, <TheButton>
won't rerender, since it will be the same throughout the component lifecycle. But of course, a simple component like the above example is too simple, and note that the component should be a pure component.
When to not use useMemo
Well, you just feel like "Hey, that means I can optimise everything by sprinkling useMemo
everywhere in my code, right?
Chotto matte! If you have this thought, you need to rethink your decision. Most of the time, useMemo
make your app less optimised than you think!
const [x, setX] = useState(0);
const [y, setY] = useState(1);
const theSum = useMemo(() => x + y, [x, y]);
Even if you can calculate x plus y using your fingers, why do you think your app needs to monitor changes for x and y, for the sake of adding x to y?
Most of the time, huge calculations are done by the backend, and the frontend will only retrieve the value from the API, and display it to the user.
So, to what level of complexity do you need to use useMemo
? The answer is, it is so rare unless you need to calculate the value of infinity during rerenders. Personally, I didn't use useMemo
, except for once, which is to calculate the effective interest rate of a loan based on several variables. Since that is a frontend-only app, so I had to use it. If you ask me, I do feel that the calculation might not need useMemo
in the first place.
In case you are wondering how to calculate effective interest rate.
Conclusion
That's it about useLayoutEffect
and useMemo
. Though they are hooks provided by React, the use case for them is not as easy as you think, thus the usage of these hooks is quite rare. At least, you know how to write them and how they work, so when you need to use these hooks by encountering the use case, you already know how to use them.
While useMemo
is keeping the cache of its dependency for it to run the function, then what is useCallback
? Well, keep yourself posted for the next article on the React hooks!
And as usual, take care of yourselves, and peace be upon ya!
Top comments (0)