React.memo, React.useMemo, React.useCallback

ref – https://dev.to/dinhhuyams/introduction-to-react-memo-usememo-and-usecallback-5ei3
https://medium.com/javascript-in-plain-english/react-usememo-and-when-you-should-use-it-e69a106bbb02
https://dmitripavlutin.com/dont-overuse-react-usecallback/

React.memo IS NOT A HOOK. React.memo is a higher order component.
It’s the same to React.PureComponent , but for function components instead of classes.

Where as React.useMemo is a hook.

If your function component renders the same result given the same props,
you can wrap it in a call to React.memo for a performance boost in some cases
by memoizing the result. This means that React will skip rendering the component,
and reuse the last rendered result.

React.memo only checks for prop changes. If your function component wrapped
in React.memo has a useState or useContext Hook in its implementation,
it will still re-render when state or context change.

We have an App with two different states, count1 and count2.

We then return a React Element with two components that uses these counts as props

As you can see, even though we click on the button to increase Counter Won’s state property count1, Counter Too gets re-rendered as well. This is because React sees that state has changed, it will re-render the whole whole element.

The heuristic algorithm says that as long as the node types are the same, then recursively update.

Hence, our main div stays the same.
The header stays the same.
Button stays the same because it’s just a click handler.
Counter Won’s state changed from 0 to 1, so we update it.
Counter Too’s state stayed the same, update also.

So the problem is here.
“Counter Too” re-renders simply because a state from Counter Won has changed which makes App re-render.

In order to separate this, we can use React.memo to control our re-rendering scheme. So instead of all instances of Counter components under a React Element being re-rendered simply due to one state change, we can have it where by default.

If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.

React.memo only checks for prop changes. If your function component wrapped in React.memo has a useState or useContext Hook in its implementation, it will still rerender when state or context change.

If these props are unchanged, React.memo will reuse the last rendered result, therefore, it prevents the component from being re-rendered. In our example, React.memo will check if there are any changes with the value and children props since the last render. Since our button only changes the value of the counter1, React.memo will prevent the counter2 from being re-rendered.

In Counter.js

If you want to use your own logic for the re-rendering

There is an example at the end here

React.useMemo

useMemo is a hook that (given a function, and an array of values) returns a memoized value. It takes a function that does some calculations. When said properties have been changed, then it recalculates the function. If they stay the same, then we don’t recalculate and simply return what was memoized from before.

The hook will return a new value only when one of the dependencies value changes (referential equality).

React.useMemo Example

We have two functional component: FibDisplay and NameDisplay.
FibDisplay has prop property length. It is connected to state length.
NameDisplay has prop property name, it is connected to state name.

Run the app. Say we update name. We set_name a new value from input. This update will trigger a re-render for all components in our App’s render, including the FibDisplay component.

However, FibDisplay component re-render is very expensive because it does a lot of calculations.

Can we somehow improve this performance?

In order to do so, we can have React memo-ize the calculations of our fib:

It will remember the value we have calculated as the result if our previous prop and current prop match.

Then when we re-run the app, you’ll notice that we still re-render FibDisplay, however, const numbers will not be re-calculated again.

All you’ll see is the React components being re-rendered. But no expensive Fib computations.

– The hook itself introduces new complex logic, and it might introduce more performance issues than it solves. Do not apply useMemo unless this is a really expensive computation, or, if you are not sure, you can benchmark both ways and make an informed descision.

– As per the React docs, you may never depend on the internal mechanisms on useMemo. In other words, while useMemo is supposed to be called only on dependencies change, this is not guaranteed. Your app must still work perfectly well (maybe a bit slow though) if useMemo calls your callback on every render.

Differences between useMemo and useEffect

  • useState is causing a re-render on the call of the setState method (second element in the array returned). It does not have any dependencies like useMemo or useEffect.
  • useMemo only recalculates a value if the elements in its dependency array change (if there are no dependencies – i.e. the array is empty, it will recalculate only once). If the array is left out, it will recalculate on every render. Calling the function does not cause a re-render. Also it runs during the render of the component and not before.
  • useEffect is called after each render, if elements in its dependency array have changed or the array is left out. If the array is empty, it will only be run once on the initial mount (and unmount if you return a cleanup function).

useCallback

Different function instances sharing the same code are often created inside React components.

When inside a React component body a function is defined (e.g. a callback or event handler), this function is re-created on every rendering:

In other words…

handleClick is a different function object on every rendering of MyComponent.

When we pass button handler functions to props of child components:

Because inline functions are cheap, the re-creation of functions on each rendering is not a problem. A few inline functions per component are acceptable.

However, there are cases when you need to keep one instance of a function:

– A component wrapped inside React.memo() (or shouldComponentUpdate) accepts a callback prop
– When the function is used as a dependency to other hooks, e.g. useEffect(…, [callback])

That’s the case when useCallback(callbackFun, depends) helps you: giving the same dependency values depends, the hook returns the same function instance between renderings:

Imagine you have a component that renders a big list of items:

MyBigList renders a list of items. Knowing the list could be big, probably a few hundreds of items. To preserve the list re-rendering, you wrap it into React.memo.

The parent component of MyBigList needs provides a handler function when an item is clicked.

handleClick callback is memoizied by useCallback(). As long as term variable stays the same, useCallback() returns the same function instance.

Even if for some reason MyParent component re-renders, handleClick stays the same and doesn’t break the memoization of MyBigList.

A bad use case would be if a components simply returns very little JSX.

Another example

where Counter component is:

Now, when App is rerendered, a new increaseCounter1 function object is passed into onClick.
ref – https://stackoverflow.com/questions/39260595/event-handlers-in-react-stateless-components

const references are popped off the local stack when the function component have been used. Thus, when we re-render this function component again, it needs to re-create the const references and the objects that they point to.

By default, React.memo does does a shallow comparison of props and objects of props. Even so, because the onClick handlers are being recreated, our child component Counter will always see that the previous and current prop’s onClick handlers are referencing different objects. Thus, it will always re-render Counter2, even if we only update Counter1’s state.

The easy way to avoid this issue is to prevent the increaseCounter2 function from being recreated when the App is re-rendered.

We make use of React.useCallback to do this:

Now, when you run it, there will only be one handler function. That way, functional component Counter will get the same previous and current function object. Thus changing Counter 1’s state will only re-render Counter1. Counter2 will be untouched.