Как использовать response-tether с Reaction-dom createPortal, чтобы иметь возможность стилизовать привязанный компонент в соответствии с компонентом, к которому он привязан? - PullRequest
0 голосов
/ 03 января 2019

У меня есть компонент, в котором я отображаю некоторые кнопки. Каждая кнопка должна иметь свою подсказку. Я бы хотел, чтобы всплывающая подсказка появлялась, когда я: кнопка наведения. Но так как моя всплывающая подсказка может содержать не только текст, я не могу использовать такие вещи, как воздушный шар или создавать его только с помощью CSS.

Идея состоит в том, чтобы использовать ответную привязь, чтобы привязать мой собственный компонент всплывающей подсказки к кнопке. Но тогда я должен справиться с этим, если при наведении курсора на кнопку появляется всплывающая подсказка. Поэтому я хочу использовать реагирующий портал для перемещения всплывающей подсказки внутри моего компонента кнопки, чтобы иметь возможность стилизовать всплывающую подсказку в соответствии с ее фактическим родителем.

Это идея, которую я уже придумал, но она не работает как есть.

    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, а кнопка - просто React.PureComponent.

Я обычно зацикливаюсь на проблеме, что Button - это JSX.Element, а не Element, поэтому я не могу использовать его в качестве целевого элемента на портале. Другое дело, что я не уверен, как использовать ref в этом случае и не лучше ли использовать id. Я открыт для любой идеи.

1 Ответ

0 голосов
/ 03 января 2019

Я думаю, что этот кодекс, который я написал, поможет вам.Он не работает точно так же, как всплывающая подсказка, но показывает, как вы можете использовать портал.

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...