Brad Woods Digital Garden

Notes / JavaScript / React / memo, useMemo, useCallback

The Warhammer 40k Adeptus Mechanicus symbol

Table of contents

    React UI library logo

    memo, useMemo, useCallback

    Planted: 

    Tended: 

    Status: decay

    Hits: 562

    Intended Audience: Front-end developers who use React

    Referential equality and expensive calculations is why memo, useMemo and useCallback are built into React. Structural types (object, array, function) declared within a React component will be a different instance every time the component renders. This leads to unnecessary re-renders. This note requires an understanding of the following:

    memo

    Imagine we had two React components; MyParent and MyChild. We render MyChild in MyParent. The parent also has a useState hook with a boolean value. We'll then get the parent to render a button that, when clicked, toggles the state from true to false and vice versa.

    The toggle button is there just to trigger a re-render of the parent. When the hook's state changes, the component with the hook will re-render. You will notice, however, that the child is also re-rendering (even though no changing props are being passed to it). This is because when a parent re-renders, so does its children.

    This child's re-rendering is what we call unnecessary re-renders. When the parent is re-rendering, nothing is changing in the child component. We can prevent this re-rendering by using memo. If a component is wrapped by memo and it is about to re-render, a check will be made. Memo will look at the props being passed to the component. If there is no change between the previous and the current, the component will not re-render.

    useCallback

    Below is a parent component with two child components. The parent keeps track of how many times the button in each child has been clicked. When a button is clicked, the state in 1 of the parent's hooks changes. This triggers a re-render on the parent, which in turn, triggers a re-render of both children.

    When we click button 1, its counter increases by one. So it makes sense that the parent and it re-render. The other button however, whose counter doesn't change, does an unnecessary re-render. We can use memo, like before, to prevent this.

    Hmm... that didn't seem to work. When button 1 is clicked, button 2 is still re-rendering. Memo is supposed to prevent re-renders on a component if its props don't change. Ahh..., but they are. The count prop is a primitive type, its not changing. However, the onClick property is a function, a structural type. When the parent re-renders, the function is replaced by a new instance. Because of referential equality check, the old onClick doesn't equal the new onClick. Thus causing the unnecessary re-render.

    useCallback will return a memoized version of the callback that only changes if one of the dependencies changes. Wrapping increment1 in this hook will maintain the instance of this function when the parent re-renders (unless 'setCount1' changes). Now, when memo looks at the onClick prop, it will check if oldOnClick1 === newOnClick1. This will return true, preventing a re-render.

    useMemo

    Below is a component that needs to do a computationally expensive calculation on some data before rendering it. The function that does this calculation is taking a long time to complete and is slowing down our app. Each time the input changes, the expensive function is called. This is normal. However, each time the component re-renders and the input doesn't change, the function is also called. Calling the function when the input doesn't change is unnecessary.

    useMemo can prevent these unnecessary calls. useMemo returns a memoized value. It will only re-call its wrapped function if the value of a dependency changes. By wrapping the call to expensiveFunction with useMemo, we prevent it from being called unless input changes.

    In computer science, a memoised function remembers all previous inputs and outputs. It never needs to perform a calculation on the same input value twice. useMemo isn't that sophiscated. It you pass it a value, then change that value, then change the value back to the original, it will do a calculation on the original value again.

    Warning

    You should not use memo, useMemo or useCallback unless you are noticing a performance problem. Performance optimisations aren't free. For example, when implementing useMemo, you free up CPU time but you pay by taking up more memory. If you need a reference to an object or array that doesn't require recalculation, useRef could be a better choice.

    Feedback

    Have any feedback about this note or just want to comment on the state of the economy?

    Where to next?

    JavaScript
    React
    Arrow pointing downYOU ARE HERE
    A Johnny Cab pilot