In my app, I'm trying to use Popper to create a tooltip over every element in the app. (Usually, I would only show a single tooltip, but for a presentation I want to show more than one).
I wrote this utility Component to attach tooltip directly to ref.
It works pretty well, but when I try to use it inside an [].map()
like regular react components, I lose all my positioning:
https://bit.dev/bit/base/atoms/ref-tooltip?example=5e81d946443f4900195606b7
import React, { Component } from 'react';
import { RefTooltip } from '@bit/bit.base.atoms.ref-tooltip'
export default class ExampleUsage extends Component {
state = { ref: [] };
handleRef = (elem) => {
if (this.state.ref.some(x => x === elem)) return;
this.setState({ ref: [elem] });
}
render() {
return (
<div>
<span ref={this.handleRef}>target</span>
{ /*
* (!)
* This .map() breaks tooltip
*
*/ }
{this.state.ref.map((elem, idx) => (
<RefTooltip key={idx} targetElement={elem}>
"tooltip"
</RefTooltip>
))}
</div>
);
}
}
//ref-tooltip.tsx
import React, { Component } from 'react';
import classNames from 'classnames';
//@ts-ignore
import createRef from 'react-create-ref';
import { createPopper, Instance, Options } from '@popperjs/core';
import styles from './ref-tooltip.module.scss';
export type RefTooltipProps = {
targetElement?: HTMLElement;
popperOptions?: Partial<Options>;
} & React.HTMLAttributes<HTMLDivElement>;
export class RefTooltip extends Component<RefTooltipProps> {
private ref = createRef();
private popperInstance?: Instance;
componentWillUnmount() {
this.destroy();
}
componentDidUpdate(prevProps: RefTooltipProps) {
const nextProps = this.props;
if (prevProps.targetElement !== nextProps.targetElement) {
this.reposition(nextProps.targetElement);
}
}
private reposition = (targetElement?: HTMLElement) => {
const { popperOptions = popperDefaultOptions } = this.props;
const popperElement = this.ref.current;
if (!targetElement) {
this.destroy();
}
if (!targetElement || !popperElement) return;
this.popperInstance = createPopper(targetElement, popperElement, popperOptions);
};
private destroy() {
if (!this.popperInstance) return;
this.popperInstance.destroy();
this.popperInstance = undefined;
}
render() {
const { className, targetElement, ...rest } = this.props;
return (
<div
{...rest}
ref={this.ref}
className={classNames(styles.tooltipWrapper, className)}
data-ignore-component-highlight
/>
);
}
}
const popperDefaultOptions: Partial<Options> = {
placement: 'top',
modifiers: [
{
name: 'flip',
enabled: false,
},
],
};
I don't understand why the .map() breaks popper. At least for an array of 1, it should behave the same. Any ideas why this isn't working?