Skip to content

Conversation

@som-sm
Copy link
Contributor

@som-sm som-sm commented May 7, 2025

The useLeaveDetection hook currently sets up the mouseleave event listener only once (due to the empty useEffect dependency). This means the hook always invokes the very first onLeave callback passed to it, rather than the latest one.

export function useLeaveDetection(
onLeave: (this: HTMLElement, ev: MouseEvent) => any,
) {
useEffect(() => {
document.documentElement.addEventListener('mouseleave', onLeave);
return () =>
document.documentElement.removeEventListener('mouseleave', onLeave);
}, []);
}


Now, this works in the documentation example because it uses the callback approach to update the state.

const [leaveCount, setLeaveCount] = React.useState(0);
useLeaveDetection(() => setLeaveCount((s) => s + 1));

However, if the state is updated directly by passing a value, then the hook breaks, because it fails to invoke the latest callback everytime the mouseleave event occurs, instead it keeps calling the initial callback (() => setLeaveCount(0 + 1)).

const [leaveCount, setLeaveCount] = React.useState(0); 
useLeaveDetection(() => setLeaveCount(leaveCount + 1));

A simple fix would be to add onLeave to the useEffect dependency array:

export function useLeaveDetection( 
   onLeave: (this: HTMLElement, ev: MouseEvent) => any, 
 ) { 
   useEffect(() => { 
     document.documentElement.addEventListener('mouseleave', onLeave); 
  
     return () => 
       document.documentElement.removeEventListener('mouseleave', onLeave); 
-   }, []);
+   }, [onLeave]);
 }

While this works, it's not ideal. Each time a new callback is passed, a new event listener is set up. Since it's easy to pass a new callback on every render, this places the burden of memoizing the callback on the user, which doesn’t make for a great API.


This PR updates the implementation to fix the stale callback issue while ensuring the listener is set up only once. I've also added a comprehensive set of tests to verify that everything works as expected.

@som-sm som-sm force-pushed the fix/useLeaveDetection-stale-callback-issue branch from 12ba846 to 46058a6 Compare May 7, 2025 16:22
@DavidHDev DavidHDev merged commit 07f9ea9 into DavidHDev:main May 22, 2025
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants