Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
React Hook recipe from https://usehooks.com
import { useState, useCallback, useRef } from "react";
// Usage
function App() {
const [hoverRef, isHovered] = useHover();
return (
<div ref={hoverRef}>
{isHovered ? '😁' : '☹️'}
</div>
);
}
// Hook
function useHover() {
const [value, setValue] = useState(false);
// Wrap in useCallback so we can use in dependencies below
const handleMouseOver = useCallback(() => setValue(true), []);
const handleMouseOut = useCallback(() => setValue(false), []);
// Keep track of the last node passed to callbackRef
// so we can remove its event listeners.
const ref = useRef();
// Use a callback ref instead of useEffect so that event listeners
// get changed in the case that the returned ref gets added to
// a different element later. With useEffect, changes to ref.current
// wouldn't cause a rerender and thus the effect would run again.
const callbackRef = useCallback(
node => {
if (ref.current) {
ref.current.removeEventListener("mouseover", handleMouseOver);
ref.current.removeEventListener("mouseout", handleMouseOut);
}
ref.current = node;
if (ref.current) {
ref.current.addEventListener("mouseover", handleMouseOver);
ref.current.addEventListener("mouseout", handleMouseOut);
}
},
[handleMouseOver, handleMouseOut]
);
return [callbackRef, value];
}
@abirmingham

This comment has been minimized.

Copy link

abirmingham commented Nov 22, 2019

Just curious... will useCallback(null) be called when the App is unmounted? Otherwise it appears that there is a memory leak here, where removeEventListener would be called on <div> rebinds but not App unmount.

EDIT: It does appear that useCallback(null) is called, which means that my concerns about the memory leak were unfounded.

@therealparmesh

This comment has been minimized.

Copy link

therealparmesh commented Jan 30, 2020

Should you use useEffect to handle subscribing/unsubscribing logic in a manner that is guaranteed to be safe by React? See https://github.com/therealparmesh/use-hovering.

@mbelsky

This comment has been minimized.

Copy link

mbelsky commented Feb 14, 2020

Thanks for sharing! I've typed this hook with Typescript, you may find it here: https://gist.github.com/mbelsky/72c1117a63489daf8e6067049d4532d0

@jjenzz

This comment has been minimized.

Copy link

jjenzz commented Mar 25, 2020

Thoughts on changing the event handlers to mouseenter/mouseleave to prevent the bubbling issue? https://codesandbox.io/s/recursing-goldberg-7mtvc

  • open console
  • hover the button edges and then;
  • hover the kitten
@mbelsky

This comment has been minimized.

Copy link

mbelsky commented Mar 25, 2020

^ this is best solution that I've found. So +1. Also it could be used in original use-hover

@therealparmesh

This comment has been minimized.

Copy link

therealparmesh commented Mar 25, 2020

I concur with mouseenter and mouseleave! Great idea.

@abirmingham

This comment has been minimized.

Copy link

abirmingham commented Mar 25, 2020

+1 - mouseenter/mouseleave is what I'm using in my implementation of this behavior.

@mkamalkayani

This comment has been minimized.

Copy link

mkamalkayani commented Apr 20, 2020

After using a similar hook to show an element on hover, I realised that I can use onMouseEnter and onMouseLeave to get the same results.

// Usage
function App() {
  const [isHovered, setIsHovered] = React.useState();

  return (
    <div 
     onMouseEnter={()=> setIsHovered(true)}
     onMouseLeave={()=> setIsHovered(false)}
     >
      {isHovered ? '😁' : '☹️'}
    </div>
  );
}
@Andrewnt219

This comment has been minimized.

Copy link

Andrewnt219 commented May 26, 2020

After using a similar hook to show an element on hover, I realised that I can use onMouseEnter and onMouseLeave to get the same results.

// Usage
function App() {
  const [isHovered, setIsHovered] = React.useState();

  return (
    <div 
     onMouseEnter={()=> setIsHovered(true)}
     onMouseLeave={()=> setIsHovered(false)}
     >
      {isHovered ? '😁' : '☹️'}
    </div>
  );
}

It's all about reusability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.