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
The
useEffect
hook in React runs when any of the dependencies in its dependency array change. In your case, theuseEffect
has a dependency on theonClose
prop. Therefore, it would run every time theonClose
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 theuseEffect
.To solve this, you can use the
useCallback
hook to ensure yourcloseDialog
function has a stable identity across renders unless its dependencies change: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 thecloseDialog
function will be stable across re-renders.The
useEffect
hook in theMyDialog
component is triggered because the dependency arrayonClose
is specified. This means that the effect will run whenever the value ofonClose
changes. Though the function reference foronClose
remains the same throughout the lifecycle of theMyDialog
component, the effect is not comparing the function reference itself, it’s comparing the value ofonClose
in each render cycle. WhenonClose
function is invoked,MyDialog
component is rendered again, and then a new instance ofonClose
function is created. This causes the value ofonClose
to change which triggers theuseEffect
hook.In javascript, this term is known as referential equality.
Example:
First render
So when react re-renders the component it re-declares this function
Second render
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
hookFor objects and arrays you can use
useMemo
hook