La mayoría de las API para información sobre herramientas en React que he visto funcionan de esta manera:
<Tooltip title="Hello World"> <span>hover me!</span> </Tooltip>Lo que eso termina creando es un contenedor alrededor de ese lapso, algo como esto:
<div> <span>hover me!</span> </div> En ese div, ahora, puede agregar sus controladores mouseenter/leave y demás, y descubrir el resto de la implementación de la información sobre herramientas. No me preocupan todas las demás funciones para implementar una información sobre herramientas robusta de React , pero más me pregunto cómo, desde una perspectiva técnica, envuelve ese <span> , pero sin agregar el <div> adicional, no puedo ver cómo puede hacerse en React Land (idealmente con algo como Portals).
Si uso React.cloneElement(child, { mouseenter: .... }) corre el riesgo de anular los controladores del mouse del niño, pero esa es una forma posible de hacerlo. (Básicamente, no sé cómo hacer super aquí, obtener los controladores de mouse aprobados y luego envolverlos con mi propio controlador de mouse de información sobre herramientas, si están presentes, ¿alguna idea?).
¿Cómo implementaría simplemente la parte vinculante de un componente de información sobre herramientas, donde agrega los controladores al elemento secundario pasado, utilizando el tipo de API anterior? Tal vez también podría cloneElement y agregar una ref personalizada o algo así, pero existe el mismo problema de anulación allí y no estoy seguro de cómo solucionarlo. Cualquier ayuda sería apreciada. Para conocer más detalles técnicos de React, me gustaría jugar con la creación de una información sobre herramientas básica y ver cómo debe abordar este problema en React-land.
Me lo imaginé:
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 }