DEV Community

Cover image for Let's Talk About Hooks - Part 2 (useLayoutEffect and useMemo)
Atif Aiman
Atif Aiman

Posted on

Let's Talk About Hooks - Part 2 (useLayoutEffect and useMemo)

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:

  1. useLayoutEffect - Yet another lifecycle hook!
  2. The difference between useEffect and useLayoutEffect, and why it matters
  3. useMemo - The memoization hook
  4. When to not use useMemo
  5. 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>]);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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 />
);
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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!

Discussion (0)