React hooks reference values comparison

Viewed 497

everyone! Imagine, I have a component SomeComponent. Every time 'b' value changes, useEffect is triggering despite 'b is not in its dependencies

const SomeComponent = () => {
  const a = [1, 2, 3] //just an example of dependency. In real life it will be a changing value
  const b = useSelector(someValueSelector)

  useEffect(() => {
    //do some staff
  }, [a])
}

Is there any way to store reference to 'a' array inside of SomeComponent? The only way I know is to create a wrapper component and pass

a = useMemo(() => [1, 2, 3], [])

as a props to

<SomeComponent a={a} />
2 Answers

The issue here is that on every re-render a's reference changes and hence the useEffect is triggered again

You can make use of useMemo inside SomeComponent to assign a memoized value to a

const SomeComponent = () => {
  const a = useMemo(() => [1, 2, 3], []); 
  const b = useSelector(someValueSelector)

  useEffect(() => {
    //do some staff
  }, [a])
}

As you may have gathered, but to be explicit for any future readers, this is due to object equality in JavaScript. Since objects are compared by reference, {} === {} evaluates to false.

Depending on your use case, you can work around this in the following ways:

  1. If the values are static and not dependent on props, define a in the same scope as the component. This removes the need to declare it as a dependency for the useEffect routine:

    const a = [1, 2, 3]
    const SomeComponent = () => {
      useEffect(() => {
        // do something with `a`
      }, [])
    }
    
  2. If the value is dynamic but changes should not cause the component to re-render, use a ref. Changing the value of the ref will not prompt React to re-render the component and the same object will be returned from useRef() on each render:
    const SomeComponent = () => {
      const ref = useRef([1, 2, 3])
      useEffect(() => {
        // do something with ref.current
      }, [ref])
    }
    
  3. Finally, if the value of a is dynamic or dependent on props that may change over time, use useMemo. Be aware that useMemo is not a semantic guarantee—React may opt to eject stored values to free memory, and so it should only be used as a performance optimization:
    const SomeComponent = () => {
      const a = useMemo(() => [1, 2, 3], [])
      useEffect(() => {
        // do something with a
      }, [a])
    }
    
Related