如何将react-tether与react-dom createPortal结合使用,以便能够根据要绑定到的组件对绑定的组件进行样式设置?

时间:2019-01-03 16:12:19

标签: javascript reactjs typescript jsx

我有一个组件,其中渲染了一些按钮。每个按钮应有其工具提示。我想在我的:hover按钮时显示工具提示。但是因为我的工具提示不仅可以包含文本,所以不能使用气球等功能,也不能仅使用CSS创建它。

这个想法是使用react-tether将我自己的Tooltip组件绑定到按钮。但是,如果我将鼠标悬停在按钮上,则会出现提示信息。因此,我想使用react-portal将工具提示移至按钮组件内部,以便能够根据工具提示的实际父项设置样式。

这是我已经想出的主意,但实际上并没有奏效。

    export const Bar: React.StatelessComponent<IBarProps> = (props) => {
    const { button1, button2} = props;

      return (
        <div>
          <TetherComponent
          ...
          >
            <Button
             ref={button1ref} //???
             ...(some props)
            >
              {button1.text}
            </Button>
            {
              createPortal(
                  <tooltip>button1.tooltipText</tooltip>,
                  button1ref)
            }
          </TetherComponent>

          <TetherComponent
          ...
          >...the same with second button
          </TetherComponent>
        </div>
    )}

其中工具提示只是React.StatelessComponent,而Button是简单的React.PureComponent。

我通常会遇到问题,Button是JSX.Element而不是Element,因此我不能将其用作门户中的目标元素。另一件事是,我不确定在这种情况下如何使用ref以及使用id是否更好。 我愿意接受任何想法。

1 个答案:

答案 0 :(得分:1)

我认为我编写的这种代码和平会对您有所帮助。 它不完全像工具提示那样工作,但是它显示了如何使用门户。

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";

class TooltipButton extends Component {
    state = {
        isHovered: false
    };

    buttonRef = React.createRef();

    onMouseOver = isOver => {
        this.setState({
            isHovered: isOver
        });
    };

    render() {
        const tooltip = (
            <div>
                {this.props.tooltip}
            </div>
        );

        return (
            <div>
                <button
                    ref={this.buttonRef}
                    onMouseEnter={() => this.onMouseOver(true)}
                    onMouseLeave={() => this.onMouseOver(false)}
                >
                    {this.props.text}
                </button>
                {this.state.isHovered ?
                    ReactDOM.createPortal(tooltip, this.buttonRef.current) :
                    undefined}
            </div>
        );
    }
}

TooltipButton.propTypes = {
    text: PropTypes.string.isRequired,
    tooltip: PropTypes.string.isRequired
};

export default TooltipButton;

此示例显示了如何使用门户,但是门户的目的是在另一个元素(例如document.body)中添加一些内容(工具提示元素),而不是在同一元素中。在这种情况下,createPortal实际上不执行任何操作。

打开通往同一房间的门户没有任何意义:)

我认为您需要执行以下操作:

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";

class TooltipButton extends Component {
    state = {
        isHovered: false
    };

    buttonRef = React.createRef();

    constructor() {
        super();
        this.tooltipElement = document.createElement('div');
    }

    onMouseOver = isOver => {
        this.setState({
            isHovered: isOver
        });
    };

    componentDidMount() {
        document.body.appendChild(this.tooltipElement);
    }

    componentWillUnmount() {
        document.body.removeChild(this.el);
    }

    render() {
        let buttonRect;
        let tooltip;
        if (this.buttonRef.current) {
            buttonRect = this.buttonRef.current.getBoundingClientRect();
            tooltip = (
                <div style={{
                    zIndex: Number.MAX_SAFE_INTEGER,
                    position: 'absolute',
                    top: buttonRect.top,
                    left: buttonRect.right,
                    backgroundColor: 'lightgrey'
                }}>
                    {this.props.tooltip}
                </div>
            );
        }

        return (
            <div>
                <button
                    ref={this.buttonRef}
                    onMouseEnter={() => this.onMouseOver(true)}
                    onMouseLeave={() => this.onMouseOver(false)}
                >
                    {this.props.text}
                </button>
                {this.state.isHovered ?
                    ReactDOM.createPortal(tooltip, this.tooltipElement) :
                    undefined}
            </div>
        );
    }
}

TooltipButton.propTypes = {
    text: PropTypes.string.isRequired,
    tooltip: PropTypes.string.isRequired
};

export default TooltipButton;