ReactTooltip getContent callback is called more than once for a ReactTooltip component with effect="solid"
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.
We're seeing the same behaviour, is this a bug or meant to happen?
I worked around it by addind fast-memoize around my function - but IMHO getContent should only get called after the event && enabled
@warrenc5 Could you share code snippet with us?
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>
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>
)
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 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.