Skip to content

Today I Learned - 2023-08-13

Posted on:August 13, 2023 at 03:09 AM

Let’s dive right in, shall we?

useMemo:

Re-rendering:

Whenever a Component is rerendered, objects and arrays declared directly within the component is also recreated/redeclared.

This is also true for function declarations like function(){} and expressions like () => {} - a new, different function is created on every re-render.

In other words: Let’s say you have a function handleSubmit within App component and you’d like to pass it to App’s child component, Section.

const App = () => {
  const handleSubmit = () => {
    // ... some code
  }

  return <Section onSubmit={handleSubmit} />;
}

Let’s assume that App has state that changes and triggers a re-render. Upon re-rendering, the function handleSubmit() is also RECREATED into a brand new, different function. This is usually … Fine. However, if you memoized <Section /> in this case, Section will think that {handleSubmit} is a different prop every time, and will be recalculated.

(If you want to store a function and not have it be re-declared into a new, different function unless its dependency changes, you would turn to useCallback).

calculateValue

Console.time

If you want to tell if a certain calculation is expensive, you can use console.time('start') and console.timeEnd('end')to measure the time spent in a piece of code.

React.dev suggests that if the overall logged time is 1ms or more, using useMemo might be a good idea. But note, useMemo doesn’t make the first render faster - it only helps you skip it from running again if the values are the same.

Don’t overuse useMemo

You should wrap the result of an operation with useMemo if re-doing or repeating that operation takes a lot of time. If the code without memoization is fast enough, you don’t necessarily need memoization.

Note about dependency array in useMemo:

If you are passing an object created directly in the component body as useMemo’s dependency array, memoization might be pointless.

Look at the code provided by React.dev below:

function Dropdown({ allItems, text }) {
  const searchOptions = { matchMode: 'whole-word', text };

  const visibleItems = useMemo(() => {
    return searchItems(allItems, searchOptions);
  }, [allItems, searchOptions]); // 🚩 Caution: Dependency on an object created in the component body
  // ...

Here, searchOptions is an object declared directly in Dropdown Component. Objects and arrays declared directly within the component body are essentially re-run and recreated into new objects when a component re-renders for any reason.

In this case, the value of visibleItems will be recalculated every time Dropdown re-renders because searchOptions is also recreated and therefore a distinctly different object from the previous searchOptions. Since searchOptions is a new object, recalculation takes place.

To remedy this, one option might be to memoize the searchOptions and pass the text as its dependency prior to passing it as visibleItems’s dependency, like so:

(Code from react.dev)

function Dropdown({ allItems, text }) {
  const searchOptions = useMemo(() => {
    return { matchMode: 'whole-word', text };
  }, [text]); // ✅ Only changes when text changes

  const visibleItems = useMemo(() => {
    return searchItems(allItems, searchOptions);
  }, [allItems, searchOptions]); // ✅ Only changes when allItems or searchOptions changes
  // ...

Alternatively, you can also place searchOptions inside the useMemo hook.

function Dropdown({ allItems, text }) {
  const visibleItems = useMemo(() => {
    const searchOptions = { matchMode: 'whole-word', text };
    return searchItems(allItems, searchOptions);
  }, [allItems, text]); // ✅ Only changes when allItems or text changes
  // ...

What happens if I don’t pass anything in the dependency array?

useMemo will be re-running the calculation every time anything changes or is re-rendered.

useCallback

return