Stateless tooltip component using React hooks and ref












1












$begingroup$


What I'm trying to create is a generic tooltip component that I can use anywhere. I decided on a stateless component and the use of hooks and ref.



Explanation 💡



Hook, because I wanted to have a flag for hover and ref to get element width and/or height to center it correctly.



Problem



Is this solution correct and will perform fast enough with the bigger amount of tooltip components on the same page (over 100)?
The solutions I've come across so far have not been satisfactory. I wonder if this idea makes a sense if so, I will develop it and publish it as an open source project (I would like to finally make a contribution! 😄)



Demo: https://danzawadzki.github.io/react-tooltip/



Repository: https://github.com/danzawadzki/react-tooltip



Tooltip.tsx



import * as React from 'react';
import './Tooltip.scss';
import {CSSTransition} from 'react-transition-group';
import {useState} from 'react';
import {useRef} from 'react';

export interface ITooltip {
/** Tooltip position. */
position?: 'top' | 'left' | 'right' | 'bottom';
/** Tooltip message content. */
message: String;
/** Array with details about columns to render. */
children: JSX.Element | Array<JSX.Element> | string;
}

export interface ITooltipStyle {
/** Distance to the left edge. */
left?: string;
/** Distance to the top edge. */
top?: string;
/** Distance to the bottom edge. */
bottom?: string;
/** Distance to the right edge. */
right?: string;
}

/**
* Tooltip component.
*
* @author Daniel Zawadzki <hello@danielzawadzki.com>
* @version 1.0.0
*/
const Tooltip: React.FunctionComponent<ITooltip> = ({position = 'top', message, children}) => {
/**
* Hook toggling tooltip isVisible flag.
*/
const [isVisible, setIsVisible] = useState<boolean>(false);
const toggle = () => setIsVisible(!isVisible);

/**
* Reference to the component to track width and height
*/
const node = useRef<any>(null);

/**
* Positioning the component using the reference
*/
let style: ITooltipStyle = {};
if (node.current && node.current.offsetWidth) {
if (position === 'top' || position === 'bottom') {
style.left = `${String(node.current.offsetWidth / 2)}px`;
} else {
style.top = `-${String(node.current.offsetHeight / 1.5)}px`;
}
}

return (
<div className="Tooltip">
<div
className="Tooltip__toggler"
onMouseOverCapture={toggle}
onMouseOut={toggle}
ref={node}>
{children}
</div>
<CSSTransition
in={isVisible}
timeout={200}
classNames="Tooltip"
unmountOnExit>
<div
className={`Tooltip__message Tooltip__message--${position}`}
style={style}>
{message}
</div>
</CSSTransition>
</div>
);
};

export default Tooltip;


I know that there are probably a lot of bugs and areas for development there, but is it any good idea?










share|improve this question











$endgroup$

















    1












    $begingroup$


    What I'm trying to create is a generic tooltip component that I can use anywhere. I decided on a stateless component and the use of hooks and ref.



    Explanation 💡



    Hook, because I wanted to have a flag for hover and ref to get element width and/or height to center it correctly.



    Problem



    Is this solution correct and will perform fast enough with the bigger amount of tooltip components on the same page (over 100)?
    The solutions I've come across so far have not been satisfactory. I wonder if this idea makes a sense if so, I will develop it and publish it as an open source project (I would like to finally make a contribution! 😄)



    Demo: https://danzawadzki.github.io/react-tooltip/



    Repository: https://github.com/danzawadzki/react-tooltip



    Tooltip.tsx



    import * as React from 'react';
    import './Tooltip.scss';
    import {CSSTransition} from 'react-transition-group';
    import {useState} from 'react';
    import {useRef} from 'react';

    export interface ITooltip {
    /** Tooltip position. */
    position?: 'top' | 'left' | 'right' | 'bottom';
    /** Tooltip message content. */
    message: String;
    /** Array with details about columns to render. */
    children: JSX.Element | Array<JSX.Element> | string;
    }

    export interface ITooltipStyle {
    /** Distance to the left edge. */
    left?: string;
    /** Distance to the top edge. */
    top?: string;
    /** Distance to the bottom edge. */
    bottom?: string;
    /** Distance to the right edge. */
    right?: string;
    }

    /**
    * Tooltip component.
    *
    * @author Daniel Zawadzki <hello@danielzawadzki.com>
    * @version 1.0.0
    */
    const Tooltip: React.FunctionComponent<ITooltip> = ({position = 'top', message, children}) => {
    /**
    * Hook toggling tooltip isVisible flag.
    */
    const [isVisible, setIsVisible] = useState<boolean>(false);
    const toggle = () => setIsVisible(!isVisible);

    /**
    * Reference to the component to track width and height
    */
    const node = useRef<any>(null);

    /**
    * Positioning the component using the reference
    */
    let style: ITooltipStyle = {};
    if (node.current && node.current.offsetWidth) {
    if (position === 'top' || position === 'bottom') {
    style.left = `${String(node.current.offsetWidth / 2)}px`;
    } else {
    style.top = `-${String(node.current.offsetHeight / 1.5)}px`;
    }
    }

    return (
    <div className="Tooltip">
    <div
    className="Tooltip__toggler"
    onMouseOverCapture={toggle}
    onMouseOut={toggle}
    ref={node}>
    {children}
    </div>
    <CSSTransition
    in={isVisible}
    timeout={200}
    classNames="Tooltip"
    unmountOnExit>
    <div
    className={`Tooltip__message Tooltip__message--${position}`}
    style={style}>
    {message}
    </div>
    </CSSTransition>
    </div>
    );
    };

    export default Tooltip;


    I know that there are probably a lot of bugs and areas for development there, but is it any good idea?










    share|improve this question











    $endgroup$















      1












      1








      1





      $begingroup$


      What I'm trying to create is a generic tooltip component that I can use anywhere. I decided on a stateless component and the use of hooks and ref.



      Explanation 💡



      Hook, because I wanted to have a flag for hover and ref to get element width and/or height to center it correctly.



      Problem



      Is this solution correct and will perform fast enough with the bigger amount of tooltip components on the same page (over 100)?
      The solutions I've come across so far have not been satisfactory. I wonder if this idea makes a sense if so, I will develop it and publish it as an open source project (I would like to finally make a contribution! 😄)



      Demo: https://danzawadzki.github.io/react-tooltip/



      Repository: https://github.com/danzawadzki/react-tooltip



      Tooltip.tsx



      import * as React from 'react';
      import './Tooltip.scss';
      import {CSSTransition} from 'react-transition-group';
      import {useState} from 'react';
      import {useRef} from 'react';

      export interface ITooltip {
      /** Tooltip position. */
      position?: 'top' | 'left' | 'right' | 'bottom';
      /** Tooltip message content. */
      message: String;
      /** Array with details about columns to render. */
      children: JSX.Element | Array<JSX.Element> | string;
      }

      export interface ITooltipStyle {
      /** Distance to the left edge. */
      left?: string;
      /** Distance to the top edge. */
      top?: string;
      /** Distance to the bottom edge. */
      bottom?: string;
      /** Distance to the right edge. */
      right?: string;
      }

      /**
      * Tooltip component.
      *
      * @author Daniel Zawadzki <hello@danielzawadzki.com>
      * @version 1.0.0
      */
      const Tooltip: React.FunctionComponent<ITooltip> = ({position = 'top', message, children}) => {
      /**
      * Hook toggling tooltip isVisible flag.
      */
      const [isVisible, setIsVisible] = useState<boolean>(false);
      const toggle = () => setIsVisible(!isVisible);

      /**
      * Reference to the component to track width and height
      */
      const node = useRef<any>(null);

      /**
      * Positioning the component using the reference
      */
      let style: ITooltipStyle = {};
      if (node.current && node.current.offsetWidth) {
      if (position === 'top' || position === 'bottom') {
      style.left = `${String(node.current.offsetWidth / 2)}px`;
      } else {
      style.top = `-${String(node.current.offsetHeight / 1.5)}px`;
      }
      }

      return (
      <div className="Tooltip">
      <div
      className="Tooltip__toggler"
      onMouseOverCapture={toggle}
      onMouseOut={toggle}
      ref={node}>
      {children}
      </div>
      <CSSTransition
      in={isVisible}
      timeout={200}
      classNames="Tooltip"
      unmountOnExit>
      <div
      className={`Tooltip__message Tooltip__message--${position}`}
      style={style}>
      {message}
      </div>
      </CSSTransition>
      </div>
      );
      };

      export default Tooltip;


      I know that there are probably a lot of bugs and areas for development there, but is it any good idea?










      share|improve this question











      $endgroup$




      What I'm trying to create is a generic tooltip component that I can use anywhere. I decided on a stateless component and the use of hooks and ref.



      Explanation 💡



      Hook, because I wanted to have a flag for hover and ref to get element width and/or height to center it correctly.



      Problem



      Is this solution correct and will perform fast enough with the bigger amount of tooltip components on the same page (over 100)?
      The solutions I've come across so far have not been satisfactory. I wonder if this idea makes a sense if so, I will develop it and publish it as an open source project (I would like to finally make a contribution! 😄)



      Demo: https://danzawadzki.github.io/react-tooltip/



      Repository: https://github.com/danzawadzki/react-tooltip



      Tooltip.tsx



      import * as React from 'react';
      import './Tooltip.scss';
      import {CSSTransition} from 'react-transition-group';
      import {useState} from 'react';
      import {useRef} from 'react';

      export interface ITooltip {
      /** Tooltip position. */
      position?: 'top' | 'left' | 'right' | 'bottom';
      /** Tooltip message content. */
      message: String;
      /** Array with details about columns to render. */
      children: JSX.Element | Array<JSX.Element> | string;
      }

      export interface ITooltipStyle {
      /** Distance to the left edge. */
      left?: string;
      /** Distance to the top edge. */
      top?: string;
      /** Distance to the bottom edge. */
      bottom?: string;
      /** Distance to the right edge. */
      right?: string;
      }

      /**
      * Tooltip component.
      *
      * @author Daniel Zawadzki <hello@danielzawadzki.com>
      * @version 1.0.0
      */
      const Tooltip: React.FunctionComponent<ITooltip> = ({position = 'top', message, children}) => {
      /**
      * Hook toggling tooltip isVisible flag.
      */
      const [isVisible, setIsVisible] = useState<boolean>(false);
      const toggle = () => setIsVisible(!isVisible);

      /**
      * Reference to the component to track width and height
      */
      const node = useRef<any>(null);

      /**
      * Positioning the component using the reference
      */
      let style: ITooltipStyle = {};
      if (node.current && node.current.offsetWidth) {
      if (position === 'top' || position === 'bottom') {
      style.left = `${String(node.current.offsetWidth / 2)}px`;
      } else {
      style.top = `-${String(node.current.offsetHeight / 1.5)}px`;
      }
      }

      return (
      <div className="Tooltip">
      <div
      className="Tooltip__toggler"
      onMouseOverCapture={toggle}
      onMouseOut={toggle}
      ref={node}>
      {children}
      </div>
      <CSSTransition
      in={isVisible}
      timeout={200}
      classNames="Tooltip"
      unmountOnExit>
      <div
      className={`Tooltip__message Tooltip__message--${position}`}
      style={style}>
      {message}
      </div>
      </CSSTransition>
      </div>
      );
      };

      export default Tooltip;


      I know that there are probably a lot of bugs and areas for development there, but is it any good idea?







      react.js typescript jsx






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 18 mins ago









      200_success

      130k16153419




      130k16153419










      asked 1 hour ago









      Dan ZawadzkiDan Zawadzki

      162




      162






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          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: "196"
          };
          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: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          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%2fcodereview.stackexchange.com%2fquestions%2f215217%2fstateless-tooltip-component-using-react-hooks-and-ref%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • 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.


          Use MathJax to format equations. MathJax reference.


          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%2fcodereview.stackexchange.com%2fquestions%2f215217%2fstateless-tooltip-component-using-react-hooks-and-ref%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

          Список кардиналов, возведённых папой римским Каликстом III

          Deduzione

          Mysql.sock missing - “Can't connect to local MySQL server through socket”