Does useMemo hooks in React.js also memoizes object reference as well?

Viewed 8937

This is common explaination about useMemo hook in React.js

useMemo(compute, dependencies) invokes compute, memoizes the calculation result, and returns it to the component.

If during next renderings the dependencies don’t change, then useMemo() doesn’t invoke compute but returns the memoized value.

- Dmitri Pavlutin

However, I saw the example of useMemo hooks that returns an array like this below.

React.useMemo(() => [1, 2, 3], []);

As you can see, useMemo returns an array which is considered as an object in Javascript.

This is confusing. I thought usage of useMemo hook is for memoizing calculated value.

So I did a little test to figure out how useMemo with object works

const Child = React.memo(({ obj }) => {

  console.log('this is child component')

  return <div>Child Component</div>
})

const App = () => {
  const [toggle, setToggle] = React.useState(false)

  const memoObj = React.useMemo(() => { return { bar: 'foo' } }, [])

  return (
    <div>
      <button onClick={() => setToggle(!toggle)}>test</button>
      <Child obj={memoObj} />
    </div>
  );
}

When memoObj is not wrapped by useMemo, whenever I click test button, I can see the message from Child component in console.

On the other hand, when memoObj is wrapped by the hook, the message shows up only when Child component rendered first time.

Does it mean that useMemo not only memoize calculated value but also reference of an object as well?

2 Answers

That is true, useMemo will always keep the reference to the object from the last renders while useCallback keep the reference of function (also an object) from the last renders

Your example is also a great way to see how useMemo works!

You could also create the same example for useCallback, this will give the same behaviour

const Child = React.memo(({ obj }) => {
   console.log('this is child component');

   return <div>Child Component</div>;
});

const App = () => {
   const [toggle, setToggle] = React.useState(false);

   const useCallbackObj = React.useCallback(() => {
      console.log('callback is called!');
   }, []);

   return (
      <div>
         <button onClick={() => setToggle(!toggle)}>test</button>
         <Child obj={useCallbackObj} />
      </div>
   );
};

Does it mean that useMemo not only memoize calculated value but also reference of an object as well?

Yes of course and does not re-render the memoized component until is necessary to re-render it (like something has changed).

BUT you have to keep in mind that:

By default it will only shallowly compare complex objects in the props object.

So

If you want control over the comparison, you can also provide a custom comparison function as the second argument.

function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);

This is what React says.

Related