useEffect not working in useFrameApi onReady event

Viewed 139

Im trying to change the url of the video dynamically using a useEffect Hook and i dont want to use third party library, so i have done this script, when i changing the url, the youtubeId state is well update but no new video is load in the player.

import React, { useEffect, useState } from "react";

const Y = () => {
  const [youtubeId, setYoutubeId] = useState("ZPt9dJw1Dbw");

  useEffect(() => {
    if (!window.YT) {
      // If not, load the script asynchronously
      const tag = document.createElement("script");
      tag.src = "https://www.youtube.com/iframe_api";

      // onYouTubeIframeAPIReady will load the video after the script is loaded

      window.onYouTubeIframeAPIReady = loadVideo;

      const firstScriptTag = document.getElementsByTagName("script")[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    } else {
      loadVideo();
    }

    function loadVideo() {
      console.log(youtubeId);
      let player = new window.YT.Player(`youtube-player-${youtubeId}`, {
        videoId: youtubeId,
        events: {
          onReady: (event) => {
            event.target.playVideo();
            event.target.loadVideoById({
              videoId: youtubeId
            });
          }
        }
      });
    }
    //
  }, [youtubeId]);

  const foo = () => {
    setYoutubeId("ZrNqjSCfL8E");
  };

  return (
    <div>
      <div id={`youtube-player-${youtubeId}`} />
      <button onClick={foo}>change video</button>
    </div>
  );
};

export default Y;

1 Answers

You have done most of work correct. If you go through youtube Iframe api they set global var. It means you have to do some changes.

  1. define player variable outside.
  2. According to your code it will retrying to set new window.YT.Player object in variable but that's not how Iframe api work. Once its set in your player object. It will give another useFull function for doing that(you can find in there docs).

Here is full code:

import { useEffect, useState, useCallback } from "react";
import "./styles.css";
// import React, { useEffect, useState } from "react";
let player;

const Y = () => {
  const [youtubeId, setYoutubeId] = useState("ZPt9dJw1Dbw");

  useEffect(() => {
    if (!window.YT) {
      // If not, load the script asynchronously
      const tag = document.createElement("script");
      tag.src = "https://www.youtube.com/iframe_api";

      // onYouTubeIframeAPIReady will load the video after the script is loaded

      window.onYouTubeIframeAPIReady = loadVideo;

      const firstScriptTag = document.getElementsByTagName("script")[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    } else {
      loadVideo();
    }

    function loadVideo() {
      if (!player) {
        player = new window.YT.Player(`youtube-player-${youtubeId}`, {
          videoId: youtubeId,
          events: {
            onReady: (event) => {
              event.target.playVideo();
              event.target.loadVideoById({
                videoId: youtubeId
              });
            }
          }
        });
      }
      if (player && player.loadVideoById) {
        player.loadVideoById({ videoId: youtubeId });
      }
    }
    //
  }, [youtubeId]);

  const foo = () => {
    setYoutubeId("ZrNqjSCfL8E");
  };

  return (
    <div>
      <div id={`youtube-player-${youtubeId}`} />
      <button onClick={foo}>change video</button>
    </div>
  );
};

export default function App() {
  return (
    <div className="App">
      <Y />
    </div>
  );
}

Here is the demo: https://codesandbox.io/s/async-smoke-084n6

Related