-
-
Save gragland/cfc4089e2f5d98dde5033adc44da53f8 to your computer and use it in GitHub Desktop.
import { useRef, useState, useEffect } from 'react'; | |
// Usage | |
function App() { | |
const [hoverRef, isHovered] = useHover(); | |
return ( | |
<div ref={hoverRef}> | |
{isHovered ? '😁' : '☹️'} | |
</div> | |
); | |
} | |
// Hook | |
function useHover() { | |
const [value, setValue] = useState(false); | |
const ref = useRef(null); | |
const handleMouseOver = () => setValue(true); | |
const handleMouseOut = () => setValue(false); | |
useEffect( | |
() => { | |
const node = ref.current; | |
if (node) { | |
node.addEventListener('mouseover', handleMouseOver); | |
node.addEventListener('mouseout', handleMouseOut); | |
return () => { | |
node.removeEventListener('mouseover', handleMouseOver); | |
node.removeEventListener('mouseout', handleMouseOut); | |
}; | |
} | |
}, | |
[ref.current] // Recall only if ref changes | |
); | |
return [ref, value]; | |
} |
Hello everyone!
I've made a Typescript version:
import { useEffect, useState, useRef } from 'react';
type THook<T extends HTMLElement> = [
React.RefObject<T>,
boolean,
];
export const useMouseHover = <T extends HTMLElement>(): THook<T> => {
const [hovered, setHovered] = useState(false);
const ref = useRef<T>(null);
useEffect(() => {
const handleMouseOver = (): void => setHovered(true);
const handleMouseOut = (): void => setHovered(false);
const node = ref && ref.current;
if (node) {
node.addEventListener('mouseover', handleMouseOver);
node.addEventListener('mouseout', handleMouseOut);
return () => {
node.removeEventListener('mouseover', handleMouseOver);
node.removeEventListener('mouseout', handleMouseOut);
};
}
}, [ref]);
return [ref, hovered];
};
Example of usage:
const [buttonRef, buttonHovered] = useMouseHover<HTMLButtonElement>();
const color = buttonHovered ? 'red' : 'blue';
return (
<button
ref={buttonRef}
style={{ color }}
>
click me
</button>
);
React Hook React.useEffect has an unnecessary dependency: 'ref.current'. Either exclude it or remove the dependency array. Mutable values like 'ref.current' aren't valid dependencies because mutating them doesn't re-render the component.
eslint (react-hooks/exhaustive-deps)
[ref.current] // Recall only if ref changes
could be changed to
[ref] // Recall only if ref changes
?
I had an issue with the last event being fired being a mouseover
event. I switched the mouseover
to mouseenter
and mouseout
to mouseleave
which solved it. Any drawbacks to this method? It also limits the amount of events firing as it doesn't fire on child elements.
I don't really think you need the useRef hook.
you could just use the useState hook and the onMouseEnter and onMouseLeave as props on the component.
what do you think?
import React,{useState,useRef,useEffect} from "react"
export default function Image({className,image}){
const [ishover, setIsHover] = useState(false)
console.log(ishover)
return(
<div
className={`${className} image-container`}
onMouseEnter={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
>
<img src={image.url} className="image-grid"/>
</div>
)
}
Hey @gragland
Current implementation re-creates
handleMouseOver
andhandleMouseOut
callbacks on every state update. There is a fix for that: https://gist.github.com/mbelsky/909c7a6b9bde3289e91a6448ae1a74b3/revisions#diff-0dd251e6c939d6c6f3846a366eade1f2