Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
React Hook recipe from
import { useState, useCallback, useRef } from "react";
// Usage
function App() {
const [hoverRef, isHovered] = useHover();
return (
<div ref={hoverRef}>
{isHovered ? '😁' : '☹️'}
// 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];

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.

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.