Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

145
Views
How to create a basic Tooltip in React where you don't actually wrap the anchor element in another DOM element?

Most of the APIs for Tooltips in React I've seen work something like this:

<Tooltip title="Hello World">
  <span>hover me!</span>
</Tooltip>

What that ends up creating is a wrapper around that span, something like this:

<div>
  <span>hover me!</span>
</div>

On that div, now, you can add your mouseenter/leave handlers and such, and figure out the rest of the tooltip implementation. I am not concerned with all the other features for implementing a robust React tooltip, but more wondering how from a technical perspective you go about wrapping that <span>, but without adding the extra <div>, I can't see how it can be done in React land (ideally with something like Portals).

If I use React.cloneElement(child, { mouseenter: .... }) you run the risk of overriding the child's mouse handlers, but that is one possible way to do it. (I don't know how to basically do super here, get the passed in mouse handlers, and then wrap them with my own tooltip mouse handler, if they are present, any ideas?).

How would you go about implementing simply the binding part of a tooltip component, where it adds the handlers to the passed in child, using the above sort of API? Maybe you could also cloneElement and add a custom ref or something, but same override problem exists there and not sure how to work around that. Any help would be appreciated. To learn more React technical details, I would like to play around with making a basic tooltip, and see how you should approach this problem in React-land.

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

I figured it out:

import ReactDOM from 'react-dom'
import React, { useState, useCallback, useEffect } from 'react'

export default function Tooltip({
  content,
  mouseEnterDelay = 200,
  mouseLeaveDelay = 500,
  children,
}) {
  const [portalContainer, setPortalContainer] = useState(typeof document !== 'undefined' && document.querySelector('#tooltip'))
  const [mouseEnterDelayTimer, setMouseEnterDelayTimer] = useState(null)
  const [mouseLeaveDelayTimer, setMouseLeaveDelayTimer] = useState(null)
  const [isVisible, setIsVisible] = useState(false)

  const child = children

  const oldMouseEnterHandler = child.props.onMouseEnter
  const oldMouseLeaveHandler = child.props.onMouseLeave

  const clearMouseLeaveTimer = useCallback(() => {
    clearTimeout(mouseLeaveDelayTimer)
    setMouseLeaveDelayTimer(null)
  }, [mouseLeaveDelayTimer, setMouseLeaveDelayTimer])

  const clearMouseEnterTimer = useCallback(() => {
    clearTimeout(mouseEnterDelayTimer)
    setMouseEnterDelayTimer(null)
  }, [mouseEnterDelayTimer, setMouseEnterDelayTimer])

  const show = useCallback(() => {
    clearMouseEnterTimer()
    setIsVisible(true)
  }, [setIsVisible, clearMouseEnterTimer])

  const hide = useCallback(() => {
    clearMouseLeaveTimer()
    setIsVisible(false)
  }, [setIsVisible, clearMouseLeaveTimer])

  const handleMouseEnter = useCallback((e) => {
    oldMouseEnterHandler?.(e)
    console.log('handleMouseEnter', mouseLeaveDelayTimer)

    if (mouseLeaveDelayTimer) {
      clearMouseLeaveTimer()
      return
    }

    const timer = setTimeout(show, mouseEnterDelay)
    setMouseEnterDelayTimer(timer)
  }, [clearMouseLeaveTimer, oldMouseEnterHandler, setMouseEnterDelayTimer, show, mouseEnterDelay, mouseLeaveDelayTimer])

  const handleMouseLeave = useCallback((e) => {
    console.log('handleMouseLeave', mouseEnterDelayTimer)
    oldMouseLeaveHandler?.(e)

    if (mouseEnterDelayTimer) {
      clearMouseEnterTimer()
      return
    }

    const timer = setTimeout(hide, mouseLeaveDelay)
    setMouseLeaveDelayTimer(timer)
  }, [clearMouseLeaveTimer, mouseEnterDelayTimer, clearMouseEnterTimer, hide, setMouseLeaveDelayTimer, mouseLeaveDelay])

  const newChild = React.cloneElement(child, {
    onMouseEnter: handleMouseEnter,
    onMouseLeave: handleMouseLeave,
  })

  if (isVisible) {
    const portal = ReactDOM.createPortal(
      <div>{content}</div>,
      portalContainer
    )
    return (
      <>
        {portal}
        {newChild}
      </>
    )
  }

  return newChild
}
about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!