DEV Community

Cover image for React Custom Hook: useHover
Sergey Leschev
Sergey Leschev

Posted on

11 1 1 3 1

React Custom Hook: useHover

In this article series, we embark on a journey through the realm of custom React hooks, discovering their immense potential for elevating your development projects. Our focus today is on the "useHover" hook, one of the many carefully crafted hooks available in the collection of React custom hooks.

Github: https://github.com/sergeyleschev/react-custom-hooks

import { useState } from "react"
import useEventListener from "../useEventListener/useEventListener"

export default function useHover(ref) {
    const [hovered, setHovered] = useState(false)
    useEventListener("mouseover", () => setHovered(true), ref.current)
    useEventListener("mouseout", () => setHovered(false), ref.current)
    return hovered
}
Enter fullscreen mode Exit fullscreen mode

This lightweight hook leverages the useState and useEventListener hooks from React to keep track of the hover state. By simply passing a ref to the useHover hook, you can start receiving accurate hover events. The hook listens for "mouseover" and "mouseout" events, updating the hovered state accordingly.

One of the key advantages of useHover is its simplicity and reusability. By encapsulating the hover logic within the hook, you can easily use it across multiple components without duplicating code. This promotes clean and maintainable code, saving you time and effort in the long run.

UseHover can be used in a variety of scenarios. Whether you need to highlight an element on hover, trigger additional actions, or dynamically change styles, this custom hook has got you covered. It provides a seamless way to enhance the interactivity and user experience of your React components.

import { useRef } from "react"
import useHover from "./useHover"

export default function HoverComponent() {
    const elementRef = useRef()
    const hovered = useHover(elementRef)
    return (
        <div
            ref={elementRef}
            style={{
                backgroundColor: hovered ? "blue" : "red",
                width: "100px",
                height: "100px",
                position: "absolute",
                top: "calc(50% - 50px)",
                left: "calc(50% - 50px)",
            }}
        />
    )
}
Enter fullscreen mode Exit fullscreen mode

To demonstrate its power, consider the HoverComponent example above. By applying the useHover hook to the elementRef, the background color of the div dynamically changes between blue and red depending on the hover state. This simple yet effective implementation showcases the potential of useHover in creating interactive and engaging UI components.

Full Version | React Custom Hooks:
https://dev.to/sergeyleschev/supercharge-your-react-projects-with-custom-hooks-pl4

Top comments (16)

Collapse
 
martinfjant profile image
Martin Falk Johansson

You're example should be done with CSS using :hover though. Doing with javascript what can be done with CSS isn't ideal.

Whether you need to highlight an element on hover, trigger additional actions, or dynamically change styles, this custom hook has got you covered.

Highlight and dynamically change styles are both examples that can easily be accomplished by CSS. If you can do it with CSS, do it with CSS. Javascripts hogs the main thread and is the most expensive resource available.

Collapse
 
sergeyleschev profile image
Sergey Leschev

Hi Martin! Thank you for sharing your thoughts. You make a valid point about utilizing CSS's ":hover" pseudo-class for simple hover effects. CSS is indeed a powerful tool for handling styling and interactions, and it's great for scenarios where the desired effects can be achieved without introducing JavaScript.

However, the React Custom Hook "useHover" is designed to offer more than just basic styling changes. While CSS's ":hover" is excellent for straightforward style adjustments, the "useHover" hook becomes particularly useful when you want to go beyond simple styling changes and incorporate more complex behavior based on hover events.

For instance, imagine you not only want to change the background color of an element on hover but also trigger an API request or toggle some other component's state. In such cases, the "useHover" hook can provide a structured way to manage these interactions without cluttering your component with event listeners and logic.

Collapse
 
martinfjant profile image
Martin Falk Johansson

That is not what you use as your example in the article: you actually suggest that people use it to change style. That is what I am arguing against. If you want both an API-request and a change of style, I would still to the hover using CSS.

It is, sadly, very common among developers, especially when they are deep into React or some other heavy framework, to over engineer things such as this.

Thread Thread
 
sergeyleschev profile image
Sergey Leschev • Edited

Overengineering can be a common pitfall. In capabilities of CSS, like ":hover", for simple styling changes and knowing when to leverage JavaScript, such as the "useHover" hook, for more intricate interactions.

But, the example in the material above should be easy to understand.

Thread Thread
 
martinfjant profile image
Martin Falk Johansson

This is a nonsense answer? The second sentence does not even make sense?

The example is easy to understand, yes, but it's an anti pattern and nowhere in your article does it mention that it should only be used for more advanced things than simple restyling. Quite the opposite: you give change of the style as a use case.

Collapse
 
skyjur profile image
Ski • Edited

Out of curiosity are you by any chance using chatgpt to help write parts of text/comments? I don't know why I'm getting these chatgpt vibes.

Thread Thread
 
martinfjant profile image
Martin Falk Johansson

You're not the only one getting he botty vibes to be honest.

Collapse
 
skyjur profile image
Ski

I would appreciate if in useEventListener the target object (ref) comes as 1st argument instead of being the last.

Collapse
 
sergeyleschev profile image
Sergey Leschev

Thank you for your feedback regarding the order of arguments in the useEventListener function. Your suggestion is noted and appreciated. While the current implementation places the target object (ref) as the last argument, you could potentially modify the function according to your preference if you are using it in your own projects. Feel free to customize the function to better suit your needs.

Collapse
 
skyjur profile image
Ski • Edited

Sorry but I don't even know what it means "noted and appreciated" just because it has become such a common reply very often used in quite the opposite way than what the words are telling. Does it mean that you agree that it should be 1st as opposed to last argument? Or does it mean that you don't care or think otherwise and just being polite? If you are not sure why I'm saying it feel free to ask why, if you have reasons behind your option I'd be interested to find out.

Thread Thread
 
sergeyleschev profile image
Sergey Leschev

The decision on the order of arguments may have been made based on various factors, such as compatibility with other hooks or best practices within the codebase. If you believe that having the target object (ref) as the first argument would be more intuitive or efficient in your use case, you're welcome to customize the function useEventListener to align with your preferences.

Thread Thread
 
skyjur profile image
Ski • Edited

I can't think of any reasonable "factors" that would suggest putting target object that is being manipulated as last argument. Can you give some ideas/examples why would that be preferred choice?

Thread Thread
 
sergeyleschev profile image
Sergey Leschev

I need more info about your project code base to answer your question. And better of course with examples ;) What are you trying to achieve with this change in the argument order.

Thread Thread
 
skyjur profile image
Ski • Edited

The reasons why it should be 1st argument - really not just my project as this is fairly fundamental - is because:

  • available event list and callback are inferred from the type of ref, inference dependants should come first, since event name depends on ref, thus event name should come after ref
  • The ref object will be modified (event listener added), passing ref as first object looks more closely to original oop approach target.addEventListener('name', () => { ... }) where the 'target' is modified. Having target as last, addEventListener('name', () => { ...}, target) is quite strange, I had not seen this practice in any major library in any programming language.
  • It is important to be able to tell visually if we have multiple event handlers on single ref target, or if it goes on multiple refs, and if ref is last argument, it is more difficult to tell it at glance

What could possibly be reason that 'ref' is last argument?

Collapse
 
respect17 profile image
Kudzai Murimi

Like it wooooow, thanks!

Collapse
 
sergeyleschev profile image
Sergey Leschev

You're welcome, Kudzai! 😊
Your appreciation encourages us to continue delving into the realm of custom hooks, uncovering gems like "useHover" that can truly elevate your development endeavors. Happy coding! 🚀