9

I am developing a react application and using reactstrap.

I am using Tooltip Component of reactstrap which requires a target attribute, a value of target element's id. This id is being geneated dynamically and seems reactstrap tooltip doesn't like it.

Component looks like:

MovieCard.jsx

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Col, Card, CardImg, CardBody, CardTitle, CardSubtitle, CardText, Button, Tooltip } from 'reactstrap';
import { LimitedTextTitle } from '../custom-styled/CustomStyledComponents';

class MovieCard extends Component {  

  constructor (props) {
    super(props);
    this.state = {
      open: false
    };
    this.toggle = this.toggle.bind(this);
  }

  toggle () {
    this.setState({
      open: !this.state.open
    })
  }

  render () {
    const { imdbID, Title, Year, Rated, Plot, Country, Poster } = this.props.movie;

    return (
  <Col md="4">
    <Card>
      <CardImg
        top
        width="100%"
        src={Poster}
        alt="blah"
      />
    </Card>
    <CardBody>
      <CardTitle>
        <LimitedTextTitle id={imdbID}>
          {`${Title} - (${Year})`}
        </LimitedTextTitle>
        <Tooltip placement='top' target={imdbID} isOpen={this.state.open} toggle={this.toggle}>
          {Title}
        </Tooltip>
      </CardTitle>
      <CardSubtitle>{`Rated: ${Rated} Country: ${Country}`}</CardSubtitle>
      <CardText>{Plot}</CardText>
      <Button>Read More</Button>
    </CardBody>
  </Col>
);
  }
}

MovieCard.propTypes = {
  movie: PropTypes.object.isRequired // eslint-disable-line
};

export default MovieCard;

Any suggestions?

react vesion 16.2.0

reactstrap 5.0.0-alpha.4

Priyank Thakkar
  • 4,752
  • 19
  • 57
  • 93
  • Looks ok. Do you have any error/warning in console ? or is the tooltip not appearing for only some items ? or is the imdb id is null/empty ? – Panther Jan 23 '18 at 15:13
  • imdbID isnot null, the error in console says, target is undefined, though if i remove tooltips and run the code above, it generates the with id attribute populated. – Priyank Thakkar Jan 23 '18 at 15:21
  • wow.. can you show me the render of `LimitedTextTitle` ? – Panther Jan 23 '18 at 15:58
  • https://github.com/priyankthakkar/open-movie-web.git here is the source code. refer to branch 'landing-page' – Priyank Thakkar Jan 24 '18 at 12:24
  • You have not rendered your id anywhere. You have passed it as a prop to the `styled-component` but it is not rendered on the page. Wrap it in a div/span and pass the `id` to it. – Panther Jan 24 '18 at 17:03
  • your tooltip id should not contain special characters and should not be empty. make sure it is string. – CyberAbhay Feb 04 '20 at 06:38

8 Answers8

9

Was dealing with a similar problem.

Adding the code as an answer because i cannot add a comment above...

Hope it will help you or anyone else who will come across this question.

Description: Use reactstrap tooltip for elements that are getting generated dynamically.

import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Button, Tooltip } from 'reactstrap';

class App extends React.Component {
  state = {};

  toggle = targetName => {
    if (!this.state[targetName]) {
      this.setState({
        ...this.state,
        [targetName]: {
          tooltipOpen: true
        }
      });
    } else {
      this.setState({
        ...this.state,
        [targetName]: {
          tooltipOpen: !this.state[targetName].tooltipOpen
        }
      });
    }
  };

  isToolTipOpen = targetName => {
    return this.state[targetName] ? this.state[targetName].tooltipOpen : false;
  };

  render() {
    return (
      <div>
        {[1, 2, 3, 4, 5, 6].map((x, i) => (
          <div key={`div-${i}`}>
            <Button color="link" id={`btn-${i}`}>
              {x}
            </Button>
            <Tooltip
              placement="right"
              isOpen={this.isToolTipOpen(`btn-${i}`)}
              target={`btn-${i}`}
              toggle={() => this.toggle(`btn-${i}`)}>
              Hello world!
            </Tooltip>
          </div>
        ))}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

react: 16.9.0

reactstrap: 8.0.1

https://codesandbox.io/embed/angry-taussig-fup7i?fontsize=14

Meir Keller
  • 511
  • 1
  • 7
  • 21
4

EUREKA I GOT IT!!! Building on Meir Keller's answer, there's no need to check if that state for the tooltip already exist. If it doesn't exist, it's false by default...

So long as state is defined, even if it's an empty state, this works.

This is using reactstrap's Popover, but it's the same concept.

import React, { Component, Fragment } from 'react';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css'
import { Container, Row, Col, Input, Button, Popover } from 'reactstrap';

class App extends Component {
  state = {};

  toggle = (target) => {
    // console.log(typeof target) // make sure this is a string
    this.setState({
      ...state,
      [target]: !this.state[target]
    });
  };

  render() {
    return (
      <Container>
      {["Hello", "Greetings"].map((name) => (

      <Row>
      <Fragment>
          <Button id={name} type="button">{name}</Button>
          <Popover placement="right" 
          isOpen={this.state[`${name}`]} 
          target={name}
          toggle={() => this.toggle(`${name}`)}>
          <PopoverBody>
            You've got mail. Did you know?
            </PopoverBody>
            </Popover>
            </Fragment>
      </Row>
      ))}
      </Container>
    );
  }
}

export default App;
hoohoo-b
  • 1,141
  • 11
  • 12
3

Create a new component in modular or component directory and paste this code

import React, { useState } from "react";
import { Tooltip } from "reactstrap";

const TooltipItem = props => {
    const { position='top', id } = props;
    const [tooltipOpen, setTooltipOpen] = useState(false);

    const toggle = () => setTooltipOpen(!tooltipOpen);

    return (
        <span>
      <span id={"tooltip-" + id}>
            {props.children}
      </span>
      <Tooltip
          placement={position}
          isOpen={tooltipOpen}
          target={"tooltip-" + id}
          toggle={toggle}
      >
          {props.title}
      </Tooltip>
    </span>
    );
};


export default TooltipItem;

Now import and use this tooltip component

import TooltipItem from "../Tooltip";

 <TooltipItem id={'edit' + data.id} title={'Edit Store'}>
    <i className="fas fa-edit pointer" onClick={() => this.onEditClick(data)}/>
  </TooltipItem>
ferozpuri
  • 266
  • 2
  • 9
0

I will Like to add an answer for it as already many people have mentioned many ways to deal with the problem. But reactStrap works perfectly fine, mistakes most of the beginners are doing that while creating id they are using special characters like:

- _ / @ and it can even be a space

Just keep the id a very simple combination of chars and numbers reactstrap will work totally fine

Ashish sah
  • 755
  • 9
  • 17
0

New component UncontrolledTooltip will solve the problem. Just use

 <UncontrolledTooltip
   placement="right"
   target={`btn-${i}`}
 >
   {props.title}
 </UncontrolledTooltip>
ChickenLoveBug
  • 113
  • 3
  • 6
0

I tried a lot of solutions and was still having trouble with Reactstrap Tooltip crashing when the target element is not in the Dom.

I combined a couple other solutions that people posted and this is the only way it worked for me. Conditional rendering FTW.

const ElementWithTooltip = ({
    dynamicIdentifier, // string, number, w/e
}): ReactElement => {
    // Target element state.
    const [isTargetReady, setIsTargetReady] = useState(false);
    
    // Target element ref.
    const tooltipRef = useRef(null);
 
    // Hook to recognize that the target is ready.
    useEffect(() => {
        const targetElement = tooltipRef.current;
        if (targetElement) {
            setIsTargetReady(true);
        }
      }, [tooltipRef.current]);

    // TSX.
    return (
        <>
            <span ref={tooltipRef}>This is the target element</span>
            {isTargetReady && <UncontrolledTooltip autohide={false} target={tooltipRef}>
                Tooltippy text stuff
            </UncontrolledTooltip>}
        </>
    );
0

The imdbID most probably is starting with digit i.e. 123abcdefghijklmno1234567890

Remember that tooltips can't work in that case when ID starts with a number i.e. the Tooltip's target cannot start with an integer.

all you need to do here is, change this:

<CardTitle>
    <LimitedTextTitle id={imdbID}>
      {`${Title} - (${Year})`}
    </LimitedTextTitle>
    <Tooltip placement='top' target={imdbID} isOpen={this.state.open} toggle={this.toggle}>
      {Title}
    </Tooltip>
</CardTitle>

to this:

<CardTitle>
    <LimitedTextTitle id={`movie-${imdbID}`}>
      {`${Title} - (${Year})`}
    </LimitedTextTitle>
    <Tooltip placement='top' target={`movie-${imdbID}`} isOpen={this.state.open} toggle={this.toggle}>
      {Title}
    </Tooltip>
</CardTitle>

You can avoid using state by simply switching to UncontrolledTooltip which handles all the toggle itself without asking you to handle that explicitly, like:

<CardTitle>
    <LimitedTextTitle id={`movie-${imdbID}`}>
      {`${Title} - (${Year})`}
    </LimitedTextTitle>
    <UncontrolledTooltip placement='top' target={`movie-${imdbID}`}>
      {Title}
    </UncontrolledTooltip>
</CardTitle>
Salman Malik
  • 923
  • 6
  • 24
0

Rendering dynamic content in tooltip in react js is very simple. Use ReactTooltip. For full understanding check below example. Here I am adding requestId in tooltip as dynamically.

{
completedTransactions.map((item, id) => (
<tr key={id + 1}>
  <td>{id + 1}</td>
  <td>
    <span data-tip={item.requestId} data-for="registerTip">
      {item.TransactionId}
    </span>
    <ReactTooltip id="registerTip" place="top" />
  </td>
  <td>{item.groupName}</td>
  <td>{item.purposeName}</td>
  <td>{dateFormat(item.update, "dd-mm-yyyy hh:mm tt")}</td>
 </tr>

)); }