skip to Main Content

The Main component is calling a Modal/Dialog.

[...]
  const closeDialog = () => setIsOpenDialog(false);
  return (
      <MyDialog
        isOpen={isOpenDialog}
        onClose={closeDialog}
      />
  );

The MyDialog code is:

[...]
  useEffect(() => {
    console.log('This removes dialog states (data)!')
  }, [onClose]);

export default function MyDialog({
  isOpen,
  onClose,
}) {
    return (
    <Dialog
      isOpen={isOpen}
      onClose={onClose}
      dialogTitle="Add/Edit"
      onClickCancel={onClose}
    >
    [...]
    </Dialog>
);
}

When I click on Cancel and call onClose, I can see that the useEffect is triggered. I would like to ask what is the reason? My understanding is that it shouldn’t since the onClose function object (from props) does not change.

3

Answers


  1. The useEffect hook in React runs when any of the dependencies in its dependency array change. In your case, the useEffect has a dependency on the onClose prop. Therefore, it would run every time the onClose prop changes.

    However, even though the onClose function doesn’t change in your code, useEffect might still run due to the way JavaScript handles function identities. In JavaScript, every time you define a function, it’s a new instance, even if it does exactly the same thing.

    So, in your case, you’re defining closeDialog as a new function in your render function every time your component re-renders. That means, from React’s perspective, onClose is a new function every time, which triggers the useEffect.

    To solve this, you can use the useCallback hook to ensure your closeDialog function has a stable identity across renders unless its dependencies change:

    const closeDialog = React.useCallback(() => setIsOpenDialog(false), []);
    

    In the example above, useCallback returns a memoized version of the function that only changes if one of the dependencies has changed. In this case, the dependencies array is empty ([]), meaning the identity of the closeDialog function will be stable across re-renders.

    Login or Signup to reply.
  2. The useEffect hook in the MyDialog component is triggered because the dependency array onClose is specified. This means that the effect will run whenever the value of onClose changes. Though the function reference for onClose remains the same throughout the lifecycle of the MyDialog component, the effect is not comparing the function reference itself, it’s comparing the value of onClose in each render cycle. When onClose function is invoked, MyDialog component is rendered again, and then a new instance of onClose function is created. This causes the value of onClose to change which triggers the useEffect hook.

    Login or Signup to reply.
  3. In javascript, this term is known as referential equality.

    Example:

    First render

    // This has an address in memory: 111
    const function1 = () =>{
    // ...
    }
    

    So when react re-renders the component it re-declares this function

    Second render

    // This has an address in memory: 112
    const function1 = () =>{
    // ...
    }
    

    So if a useEffect has function1 as a dependency it will compare the declaration in the Second Sender with the declaration in the First Render.

    So it will result in 111 !== 112
    Then depending on this condition it will re-run the useEffect.

    To solve this issue:

    For functions: you can use the useCallback hook

    const function1 = useCallback(()=>{
    // ...
    },[])
    

    For objects and arrays you can useuseMemo hook

    const object1 = useMemo(()=>({name: 'test user'}),[])
    const array1 = useMemo(()=>([1, 2, 3, 4]),[])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search