1

Problem fixed and codes updated

Put youtubeReady Promise into another file(youtubeReady.js) and import from it :

const YTReady = new Promise( (resolve) => {
  window.onYouTubeIframeAPIReady = () => resolve(window.YT);
});

export default YTReady;

Code updated marked files


Post updated:

I have tried the potential solution according to HMR's answer.

But it didn't work...

(codes below have been updated as well)

I replace the original codes:

this.player = new Promise (...)
this.player.then(...)

with a new approach

const youtubeReady = new Promise(...)
youtubeReady.then(...)

The result is the video did not even shows up in firstCmp nor secondCmp class obj(constructor).

I used console.log(this) in componentDidMount() of these 2 components and no video was loaded.

Did I do it right? or I missed something?


Original Post content

Well, last few days I solved my previous problems to get Youtube-api working via mouseOver/mouseLeave events.

Now I got a new problem which I am struggling with for 2 days.

I got 3 components.

First to set script tag to get api (app.js) :

import React, {Component} from "react";
import ReactDom from "react-dom";
import './app.scss';

import FirstCmp from './firstCmp.js'
import secondCmp from './secondCmp.js'

class App extends Component {

  componentDidMount() {
    let tag = document.createElement('script');
        tag.src = 'https://www.youtube.com/iframe_api';
    let firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  }

  render() {
    return (
        <div id="app">
          <FirstCmp />
          <SecondCmp />
        </div>
    )
  }
}

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

Then to create a video (firstCmp.js):

import React, { Component } from 'react';
import style from './firstCmp.scss';
import YTReady from './resource/youtubeReady';

export default class FirstCmp extends Component {

  constructor(props) {

    super(props);
    // just for example, not real code
    this.state = {
      iframeId: "myIframe",
      src: "src"
    }

  }

   componentDidMount() {

     let youtubeReady = YTReady;

     youtubeReady.then( (YT) => {
       this.player = new YT.Player(this.state.iframeId, {
          events: {
            'onReady': () => this.onPlayerReady(),
            'onStateChange': () => this.onPlayerStateChange()
          }
       })
     })
   }


   mouseOver() {

      this.player.playVideo();

   }

   mouseLeave() {

      this.player.pauseVideo();

   }

   render() {

     return (
      <section className={style.firstCmpContent}>

        <iframe
          id={this.state.iframeId}
          src={`${this.state.src}`}
          frameBorder="0"
          style={style.firstCmp}
          onMouseOver={ () => this.mouseOver() }
          onMouseLeave={ () => this.mouseLeave() } >
        </iframe>

      </section>
    );
   }
}

So far so good. The first video works fine. Until I create the second video...(secondCmp.js)

import React, { Component } from "react";
import style from './secondCmp.scss';
import YTReady from './resource/youtubeReady';


export default class SecondCmp extends Component {
    constructor(props) {

        super(props);
        // just for example, not real code
        this.state = {
          iframeId: "myIframe",
          src: "src"
        }

    }

    componentDidMount() {

      let youtubeReady = YTReady;

      youtubeReady.then( (YT) => {
        this.player = new YT.Player(iframeId, {
           events: {
              'onReady': () => this.onPlayerReady(),
              'onStateChange': () => this.onPlayerStateChange()
           }
        })
      })
    }


   mouseOver() {

      this.player.playVideo();

   }

   mouseLeave() {

      this.player.pauseVideo();

   }

   render() {

     return (
      <section className={style.secondCmpContent}>

        <iframe
          id={this.state.iframeId}
          src={`${this.state.src}`}
          frameBorder="0"
          style={style.secondCmp}
          onMouseOver={ () => this.mouseOver() }
          onMouseLeave={ () => this.mouseLeave() } >
        </iframe>

      </section>
    );
   }
}

Now, only the second video works, the first one gets promise pending!

If I create a third video, then the second and first get promise pending and only the new one(third) is working and so on.

How do I fix this problem?

Cho Lin Tsai
  • 87
  • 1
  • 1
  • 10
  • Possible duplicate of [onYouTubeIframeAPIReady called once but multiple videos needed on a page](https://stackoverflow.com/questions/17839356/onyoutubeiframeapiready-called-once-but-multiple-videos-needed-on-a-page) – HMR Apr 10 '18 at 09:35

1 Answers1

1

Create a global promise because onYouTubeIframeAPIReady is called when scripts are loaded and that should happen only once, it will be ready every time after that you can find a similar question here. Currently you are overwriting the onYouTubeIframeAPIReady with a different function every time.

So you can have one promise or service (not one for each component):

  const youtubeReady = new Promise( (resolve) => {
    window.onYouTubeIframeAPIReady = () => resolve(window.YT);
  });

Then in your component:

  youtubeReady.then( (YT) => {
    this.player = new YT.Player(iframeId, {//not sure where iframeId comes from
       events: {
          'onReady': () => this.onPlayerReady(),
          'onStateChange': () => this.onPlayerStateChange()
       }
    })
  })
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thanks for your tips. I'll try them now. – Cho Lin Tsai Apr 10 '18 at 09:58
  • I tried, but it did not work. I have updated the post content as well. – Cho Lin Tsai Apr 10 '18 at 11:47
  • `youtubeReady` should be global and there should be only one, I think each component has it's own `youtubeReady`. Are there any errors? – HMR Apr 10 '18 at 12:40
  • No, no errors. These 3 components are separated files. app.js, firstCmp.js and secondCmp.js. app.js is the main file for importing others. Does `youtubeReady` have to be global? I mean just outside of class. Or be global you mean to put `youtubeReady` in add.js? – Cho Lin Tsai Apr 10 '18 at 13:05
  • I updated codes to make more specific. Thanks for your patient – Cho Lin Tsai Apr 10 '18 at 13:49
  • @ChoLinTsai doesn't have to be but needs to be singleton. You can export it from a file – HMR Apr 10 '18 at 15:39
  • 1
    Hey, It worked!!! I put the `youtubeReady` into another file and import it. I think you were right about the `onYouTubeIframeAPIReady` can only be called once. The second time would overwrite the previous one. That's the reason why only the newest loaded video works. Thank you so much @HMR! – Cho Lin Tsai Apr 11 '18 at 06:58