Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useMemo with an array dependency?

Tags:

reactjs

Can you have an array as a dependancy for the useMemo hook?

I know this would be fine:

const dep1 : string = '1'

const thing = useMemo(()=>{
  // stuff
},[dep1])

But what about this?:

const dep2 : string[] = ['1', '2']

const thing2 = useMemo(()=>{
  // stuff
},[dep2])

I thought that I read once that objects and arrays will always return false for the equality check, effectively meaning thing2 will never be memoized. However I can't see this in the docs:

https://reactjs.org/docs/hooks-reference.html#usememo

This is a contrived example as dep1 and dep2 are constant variables but imagine they were a prop where the value would change.

like image 835
Evanss Avatar asked Dec 03 '20 12:12

Evanss


People also ask

Can useMemo return an array?

This is exactly what the useMemo hook does! It will return a memoized value from the provided function unless one of the values in the dependency array changes.

Can useEffect dependency be an array?

The React hooks that have dependency arrays are: useEffect. useLayoutEffect. useCallback.

Does useMemo work with objects?

If you want to optimize useMemo you can use object properties in the useMemo dependency array.


1 Answers

I think I was getting confused by React.memo, from the docs:

By default it will only shallowly compare complex objects in the props object. 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);

useMemo appears to work fine with an array as a dependancy:

https://codesandbox.io/s/mystifying-cori-jr0vi?file=/src/App.js:0-647

import React, { useState, useMemo } from "react";
import "./styles.css";

export default function App() {
  console.log("App rendered");
  const [st, setStr] = useState("0");
  const [arr, setArr] = useState([1]);

  const thingToRender = useMemo(() => {
    console.log("thingToRender ran");
    return `Array length is ${arr.length}`;
  }, [arr]);

  return (
    <div className="App">
      <button onClick={() => setStr(`${Math.random()}`)}>Change str</button>
      <h1>{st}</h1>
      <p>{thingToRender}</p>
      <button onClick={() => setArr([...arr, Math.round(Math.random() * 10)])}>
        Change arr
      </button>
    </div>
  );
}

However it's worth being aware that it wont work if you map (or other methods that create a new array):

https://codesandbox.io/s/silent-silence-91j8s?file=/src/App.js

import React, { useState, useMemo } from "react";
import "./styles.css";

export default function App() {
  console.log("App rendered");
  const [st, setStr] = useState("0");
  const [arr, setArr] = useState([1]);

  const arr2 = arr.map((item) => item);

  const thingToRender = useMemo(() => {
    console.log("thingToRender ran");
    return `Array length is ${arr2.length}`;
  }, [arr2]);

  return (
    <div className="App">
      <button onClick={() => setStr(`${Math.random()}`)}>Change str</button>
      <h1>{st}</h1>
      <p>{thingToRender}</p>
      <button onClick={() => setArr([...arr, Math.round(Math.random() * 10)])}>
        Change arr
      </button>
    </div>
  );
}
like image 198
Evanss Avatar answered Sep 24 '22 04:09

Evanss