Search code examples
reactjsreact-bootstrap

How can I get tooltip overlay to render in correct position initially?


I am writing a react bootstrap app where a button appears at the end of an animation. When the button first appears, I want a tooltip to appear next to it. Once the user hovers over and off the button, the tooltip should go away and re-appear whenever the user hovers over the button again. I've gotten this to work as desired, but when I position the button where I want it to be (fixed to the bottom of the screen), the tooltip initially renders in the wrong position (where the button was before being positioned).

I have created two simple examples to demonstrate this behavior.

In the first example, the button is positioned at the top of the screen and the tooltip behaves as this desire:

function Testing(){
    const refLink = useRef(null);

    const [overlayOpen, setOverlayOpen]  = useState(true); // Initially open
    const [textDone, setTextDone] = useState(false);
    useEffect(() => {
        const timeoutId = setTimeout(() => {
            setTextDone(true)
        },1000)
        return () => clearTimeout(timeoutId)
    },[])
    return(
        <Container>
            <Row>
                {textDone ?
                    <>
                    <Button ref={refLink} onMouseOver={() => setOverlayOpen(true)}
                            onMouseOut={() => setOverlayOpen(false)}
                    >
                        <FaComment/>
                    </Button>
                    <Overlay target={refLink.current} show={overlayOpen}
                             onHide={() => setOverlayOpen(false)} rootClose placement={"bottom"}>
                        <Tooltip>
                            Submit a personal response!
                        </Tooltip>
                    </Overlay>
                    </>
                    : ''
                }
            </Row>
        </Container>
   )
}

export default Testing

The textDone state is set to true once the "animation" finishes. To simplify the example, I omitted the animation itself and recreated the delay with setTimeout.

The second example is identical except for the following .css class added to the Row element in which the button, overlay, and tooltip are nested inside of.

.fixed-button{
    position:fixed;
    right: 20px;
    bottom: 20px;
}

With this addition, the button ends up where I want it, but the tooltip first appears at the top of the screen. Once I hover over the button, the tooltip moves to the correct position.

Here are some gifs of the two examples.

Example 1 (tooltip behaving as intended but button not positioned in right spot)

Example 2 (button positioned where I want it but tooltip initially showing up in wrong spot)

My question is: how can I get the tooltip to initially appear in the correct position?


Solution

  • I figured it out! As pointed out by Thamy, the issue has to do with the refLink not pointing to the button until onMouseOver is triggered. The tooltip appeared in the wrong spot while linkRef.current was still null, and wouldn't reposition itself until onMouseOver.

    I can't fully explain why, but moving the linkRef from the button component to the row component fixed my whole issue.

    <Row className={"fixed-button"} ref={refLink}>