How to use react-tether with react-dom createPortal to be able to style tethered component according to the...





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







1















I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.



The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.



This is the idea I already came up with, but it doesn't work as it is.



    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>
)}


where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.



I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
I'm open to any idea.










share|improve this question





























    1















    I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.



    The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.



    This is the idea I already came up with, but it doesn't work as it is.



        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>
    )}


    where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.



    I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
    I'm open to any idea.










    share|improve this question

























      1












      1








      1








      I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.



      The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.



      This is the idea I already came up with, but it doesn't work as it is.



          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>
      )}


      where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.



      I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
      I'm open to any idea.










      share|improve this question














      I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.



      The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.



      This is the idea I already came up with, but it doesn't work as it is.



          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>
      )}


      where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.



      I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
      I'm open to any idea.







      javascript reactjs typescript jsx






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 3 at 16:12









      Linda LangerováLinda Langerová

      366




      366
























          1 Answer
          1






          active

          oldest

          votes


















          1














          I think this peace of code I wrote will help you.
          It doesn't work exactly like tooltip, but it shows how you can use portal.



          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;


          This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.



          It doesn't have any sense to open portal to the same room :)



          I think you need to do something like this:



          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;





          share|improve this answer


























          • Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.

            – Linda Langerová
            Jan 7 at 15:03












          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54025981%2fhow-to-use-react-tether-with-react-dom-createportal-to-be-able-to-style-tethered%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          I think this peace of code I wrote will help you.
          It doesn't work exactly like tooltip, but it shows how you can use portal.



          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;


          This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.



          It doesn't have any sense to open portal to the same room :)



          I think you need to do something like this:



          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;





          share|improve this answer


























          • Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.

            – Linda Langerová
            Jan 7 at 15:03
















          1














          I think this peace of code I wrote will help you.
          It doesn't work exactly like tooltip, but it shows how you can use portal.



          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;


          This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.



          It doesn't have any sense to open portal to the same room :)



          I think you need to do something like this:



          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;





          share|improve this answer


























          • Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.

            – Linda Langerová
            Jan 7 at 15:03














          1












          1








          1







          I think this peace of code I wrote will help you.
          It doesn't work exactly like tooltip, but it shows how you can use portal.



          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;


          This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.



          It doesn't have any sense to open portal to the same room :)



          I think you need to do something like this:



          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;





          share|improve this answer















          I think this peace of code I wrote will help you.
          It doesn't work exactly like tooltip, but it shows how you can use portal.



          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;


          This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.



          It doesn't have any sense to open portal to the same room :)



          I think you need to do something like this:



          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;






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 3 at 19:20

























          answered Jan 3 at 18:18









          ValeriiValerii

          9932620




          9932620













          • Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.

            – Linda Langerová
            Jan 7 at 15:03



















          • Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.

            – Linda Langerová
            Jan 7 at 15:03

















          Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.

          – Linda Langerová
          Jan 7 at 15:03





          Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.

          – Linda Langerová
          Jan 7 at 15:03




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54025981%2fhow-to-use-react-tether-with-react-dom-createportal-to-be-able-to-style-tethered%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          MongoDB - Not Authorized To Execute Command

          in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith

          How to fix TextFormField cause rebuild widget in Flutter