react-tooltip icon indicating copy to clipboard operation
react-tooltip copied to clipboard

ReactTooltip getContent callback is called more than once for a ReactTooltip component with effect="solid"

Open tonix-tuft opened this issue 6 years ago • 7 comments

After rendering the following component:

export default class TestComponent extends React.PureComponent {

    constructor(props) {
        super(props)

        this.tooltipId = elementUniqueId() // Creates a unique ID. I checked the DOM and this ID is always unique.
    }

    render() {
        const tooltip = 'TEST!!!'

        console.warn('render()')

        return (
            <div>
                <span data-tip={tooltip}
                    data-for={this.tooltipId}>
                    HOVER ME!
                </span>
                <ReactTooltip id={this.tooltipId}
                    effect="solid"
                    place="right"
                    getContent={function() {
                        console.warn('getContent()')
            
                        return (
                            <div>{tooltip}</div>
                        )
                    }} />
            </div>
        )
    }

}

I see the following in the console:

> console.warn('render()') // OK
> console.warn('getContent()') // OK

At this point, the tooltip hasn't been shown yet. From this point on, each time I hover the span element to trigger the tooltip, I can see in the console that getContent() is always called 3 times when the mouse hovers the element showing the tooltip and 2 times when the mouse leaves the element:

// I hover the span element (the tooltip shows):
> console.warn('getContent()') // OK
> console.warn('getContent()') // NOT OK. Why is the callback called again?
> console.warn('getContent()') // NOT OK. And again?

// I move the mouse out of the span element (the tooltip hides):
> console.warn('getContent()') // NOT OK. Why does it call getContent in the first place if the tooltip is hiding?
> console.warn('getContent()') // NOT OK. Why does it call getContent in the first place if the tooltip is hiding?

Also, I noticed that getContent is called several times if I resize the browser's page even if the tooltip is not showing.

Am I missing something or this is the actual behaviour and if so why does it work like this?

Anyway, I would like to that you for this component and for your attention.

tonix-tuft avatar Aug 08 '19 07:08 tonix-tuft

We're seeing the same behaviour, is this a bug or meant to happen?

deiga avatar Nov 17 '20 11:11 deiga

I worked around it by addind fast-memoize around my function - but IMHO getContent should only get called after the event && enabled

warrenc5 avatar Feb 12 '21 00:02 warrenc5

@warrenc5 Could you share code snippet with us?

tonix-tuft avatar Feb 12 '21 08:02 tonix-tuft

pretty stock standard keyId is a long string but we think it's unique

 <a data-tip data-for={keyId} style={{color: 'red'}}>L</a>
                       
                        <ReactTooltip
                            id={keyId}
                            place="right"
                            type="info"
                            effect="solid"
                            className="customTheme" >
                            <table>
                                <tbody>
                                    {this.renderCrewLocation(trip.crewLocation, trip.serviceDate, keyId)}
                                </tbody>
                            </table>
                        </ReactTooltip>

warrenc5 avatar Feb 16 '21 05:02 warrenc5

I was calling the memoize function 200 times _M_renderCrewLocation = memoize(this.renderCrewLocation)

I moved the memoize into the constructor

return (
                    <span style={{paddingLeft: "3px"}}>
                        <a data-tip data-for={keyId} style={{color: 'red'}}>L</a>
                       
                       {
                        <ReactTooltip
                            id={keyId}
                            place="right"
                            type="info"
                            effect="solid"
                            delayUpdate={100}
                            border={false}
                            className="customTheme"
                            getContent={keyId => trip != null ? this._M_renderCrewLocation(keyId,trip) : ""}
                            /> 
                            //https://github.com/wwayne/react-tooltip/issues/506
                        }

                    </span>
                    )

warrenc5 avatar Feb 16 '21 06:02 warrenc5

I had a grid of elements with tooltips on each item debouncing getContent and keeping only trailing one did the job:

import debounce from 'lodash/debounce';

const MyComponent = () => {
  const debouncedGetContent = debounce(() => {....}, 500, { leading: false, trailing: true });
  ...
  return (
  <>
    <ReactTooltip
      effect="solid"
       multiline
       delayShow={1000}
       delayHide={0}
       getContent={debouncedGetContent}
    />
  </>
  )
}

kurochkinSergei avatar Jun 25 '21 14:06 kurochkinSergei

@kurochkinSergei The debounced callback is executed asynchronously in a timeout (either requestAnimationFrame or setTimeout for lodash). As you are using trailing: true, I guess that the first time react-tooltip calls getContent the result of the call is undefined. Then, only after 500ms will the debounced callback get called, but how are you guaranteed to return the correct result to getContent this way? Just asking because I am curious, thank you.

tonix-tuft avatar Jun 29 '21 19:06 tonix-tuft