Search code examples
reactjsvimeo

How to render popup for specific element in react?


I have a list of items and for every item (.item-wrapper) i want to get a video popup with unique videoId.

I have prepared videoPopup component for that but for every item I get the the last 'videoId' of 'elements' array (the same videoId for every item).

On the other hand when I am not using PopupVideo component and just loop through items iframes I get the proper id for specific item - that is just for test purpose. (The commented out line)

I am super new to React so I am aware that problem may be also super easy to solve.

Thanks!

Code for displaying items:


class Training extends Component {
  constructor(props) {
    super(props);
    this.state = {
      popupShowed: false
    };
  }

  togglePopup = event => {
    this.setState({
      popupShowed: !this.state.popupShowed
    });
  };

  onClosePopup = () => {
    this.togglePopup();
  };

  content = () => {
    const elements = ["76979871", "72675442", "337398380"];

    const items = [];

    for (const [index, value] of elements.entries()) {
      items.push(
        <div className="item-wrapper d-flex mb-4" key={index}>
          <div className="item-col training-num text-white font-weight-normal d-flex align-items-center justify-content-center">
            <span>{index < 10 ? "0" + index : index}</span>
          </div>
          <div className="item-col desc-col">
            <h3 className="text-white title font-weight-normal">
              Dlaczego warto?
            </h3>
            <div className="text-wrapper training-desc text-white">
              <p>
                Dowiesz się dlaczego Social Media Ninja to Zawód Przyszłości.
                Dostaniesz wiedzę na temat oferowania i briefowania klientów i
                dowiesz się jak zarabiać na social mediach.
              </p>
            </div>
          </div>
          <div className="item-col text-white time-col">
            <div className="inside-wrapper">
              <p className="text-nowrap">
                <strong>Czas trwania:</strong> 2:25:00
                <br />
                <strong>Twój postęp:</strong> 90%
              </p>
            </div>
          </div>
          <div className="item-col play-col d-flex align-items-center justify-content-center d-flex align-items-center justify-content-center">
            <div className="play-wrapper" onClick={this.togglePopup}>
              <svg
                enableBackground="new 0 0 60.001 60.001"
                viewBox="0 0 60.001 60.001"
                xmlns="http://www.w3.org/2000/svg"
                className="play-button"
              >
                <path d="m59.895 58.531-29-58c-.34-.678-1.449-.678-1.789 0l-29 58c-.155.31-.139.678.044.973.182.294.504.474.85.474h58c.347 0 .668-.18.851-.474.182-.295.199-.663.044-.973zm-57.277-.553 27.382-54.764 27.382 54.764z" />
              </svg>
              <span className="text-white mt-2 d-inline-block">zobacz</span>
              {/* <iframe src={'https://player.vimeo.com/video/' + value} width="500" height="600" frameBorder="0" allowFullScreen mozallowfullscreen="true" allowFullScreen></iframe> */}
            </div>
          </div>
          {this.state.popupShowed ? (
            <PopupVideo videoId={value} closePopup={this.onClosePopup} />
          ) : null}
        </div>
      );
    }

    return <div className="list-wrapper">{items}</div>;
  };
  render() {
    return <Layout content={this.content()} />;
  }
}

export default Training;

Code for displaying popupVideo:

class PopupVideo extends Component {
  componentDidMount = () => {
    var iframe = document.querySelector("iframe");
    var player = new Player(iframe);

    player.on("play", function() {
      console.log("played the video!");
    });
  };

  render() {
    return (
      <div className="popup video-popup">
        <div className="popup-inner d-flex align-items-center d-flex justify-content-center">
          <div className="video">
            <span
              onClick={this.props.closePopup}
              className="close-video d-flex align-items-center justify-content-center"
            >
              <img
                src=""
                alt="close video"
              />
            </span>
            <div className="embed-container">
              <iframe
                src={
                  "https://player.vimeo.com/video/" +
                  this.props.videoId +
                  "?api=1&amp;autoplay=0#t=0"
                }
                title="Nazwa szkolenia"
                frameBorder="0"
                allowFullScreen
                mozallowfullscreen="true"
                allowFullScreen
              ></iframe>
            </div>
            <div className="video-nav">
              <div className="video-progress"></div>
              <div className="d-flex align-items-center py-4">
                <div className="play">
                  <span className="play-video"></span>
                </div>
                <div className="stop">
                  <span className="stop-video"></span>
                </div>
                <div className="volume">
                  <span className="volume-video"></span>
                </div>
                <div className="time">00:00 / 05:50</div>
                <div className="break"></div>
                <div className="title">
                  <h4>
                    <strong className="mr-3 pr-3">01</strong>Dlaczego warto ?
                  </h4>
                </div>
                <div className="button">
                  <button className="btn btn-secondary d-flex justify-content-center text-uppercase text-light font-weight-bold px-4">
                    Zobacz następny
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

export default PopupVideo;

I do not have any error messages in the console.


Solution

  • Okay so i simplified the components to show you a good pattern to handle this kind of problems.

    First create a VideoContainer to hold all the videoIds in the state.

    We are going to return an array of Video components and pass the videoId as props to each one.

    This container will be responsible to just provide the data to the other components

    class VideoContainer extends Component {
      state = {
        videoIds: ["76979871", "72675442", "337398380"]
      };
    
      renderVideo = videoId => <Video key={videoId} videoId={videoId} />
    
      render() {
        const { videoIds } = this.state;
        return videoIds.map(this.renderVideo);
      }
    }
    

    Then create a Video component which will hold the popupShowed in state and will include the PopupVideo component only when popupShowed is true using && pass in the videoId and the togglePopup handler as props.

    Now every Video is an independent component which hold the state for showing the PopupVideo

    class Video extends Component {
      state = {
        popupShowed: false
      };
    
      togglePopup = () => {
        this.setState(prevState => ({
          popupShowed: !prevState.popupShowed
        }));
      };
    
      render() {
        const { popupShowed } = this.state;
        const { videoId } = this.props;
    
        return (
          <div className="item-wrapper d-flex mb-4">
            <button onClick={this.togglePopup}>Show Popup</button>
            {popupShowed && (
              <PopupVideo videoId={videoId} closePopup={this.togglePopup} />
            )}
          </div>
        );
      }
    }
    

    And last PopupVideo

    const PopupVideo = ({ videoId, closePopup }) => (
      <div className="popup video-popup">
        <span onClick={closePopup}>
          <img
            src="https://rahimblak.com/images/video-close.png"
            alt="close video"
          />
        </span>
        <div className="embed-container">
          <iframe
            src={
              "https://player.vimeo.com/video/" +
              videoId +
              "?api=1&amp;autoplay=0#t=0"
            }
            title="Nazwa szkolenia"
            frameBorder="0"
            allowFullScreen
            mozallowfullscreen="true"
          />
        </div>
      </div>
    );
    

    sandbox