Ensure that nested fields where the path is defined by a variable trigger React.useMemo update when added to the dependency array

Viewed 547

I use TypeScript 4 and React 17

I had this code in a custom hook:

  const myValue = useMemo((): string[] => {
    if (someCondition) {
      return (myMap?.field && myMap.field.[subfieldId]) || null
    } else {
      ...
    }
  }, [myMap.field.[subfieldId]])

ESlint complained that myMap.field.[subfieldId] had to be moved in a variable to be checked progammatically.

So I changed to this:

  const currentData: string[] | null = (myMap?.field && myMap.field.[subfieldId]) || null

  const myValue = useMemo((): string[] => {
    if (someCondition) {
      return currentData
    } else {
      ...
    }
  }, [currentData])

I'm not sure that letting the variable in the upper scope, being recalculated at each rendering is a good practice, I'm even worried that an infinite loop could be created.

ESLInt suggest me to to this instead, that is much more simple:

  const myValue = useMemo((): string[] => {
    if (someCondition) {
      return (myMap?.field && myMap.field.[subfieldId]) || null
    } else {
      ...
    }
  }, [myMap.field])

Will myMap.field be enought for React to find out that myMap.field[subfieldId] change? I guess it shall be. Indeed, React will not know what changed if I only put myMap, because the object reference itself does not change, but when comparing the previous and the new myMap.field, React check all the sub-fields. Does it?

1 Answers

According the react hook linter:

React Hook React.useMemo has an unnecessary dependency: 'myMap'. Either exclude it or remove the dependency array. Outer scope values like 'myMap' aren't valid dependencies because mutating them doesn't re-render the component. (react-hooks/exhaustive-deps)

So you should be fine with this:

const myValue = useMemo((): string[] => {
  if (someCondition) {
    return myMap?.field[subfieldId] || null
  } else {
    // ...
  }
}, [subfieldId]);
Related