skip to Main Content

https://codesandbox.io/p/sandbox/s82tlh?file=%2Fsrc%2FApp.tsx%3A14%2C1

I created a Youtube Player Frame and im using it inside of my Player React.Component. The first time where i call the Component, the Player loads normal but if I switch the component and go a second time to the Player Component, the Youtube Player Frame is not rendered…

declare global {
  interface Window {
    onYouTubeIframeAPIReady: () => void;
    YT: any;
  }
}

import React, { useEffect, useRef } from 'react';

interface PlayerFrameProps {
  videoId: string;
  width: string;
  height: string;
  onStateChange?: () => void;
}

const PlayerFrame: React.FC<PlayerFrameProps> = ({ videoId, width, height, onStateChange }) => {
  const playerRef = useRef(null);

  useEffect(() => {
    const loadYouTubeAPI = () => {
      if (!document.getElementById('youtube-api')) {
        const tag = document.createElement('script');
        tag.id = 'youtube-api';
        tag.src = "https://www.youtube.com/iframe_api";
        const firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
      }
    };

    loadYouTubeAPI();
    
    window.onYouTubeIframeAPIReady = () => {
      playerRef.current = new window.YT.Player('player', {
        height: height,
        width: width,
        videoId: videoId,
        events: {
          'onReady': onPlayerReady,
          'onStateChange': onPlayerStateChange
        }
      });
    };

    const onPlayerReady = (event: any) => {
      event.target.playVideo();
    };

    const onPlayerStateChange = (event: any) => {
      if (event.data === window.YT.PlayerState.ENDED) {
        if (onStateChange) {
          onStateChange();
        }
      }
    };

    return () => {
      if (playerRef.current) {
        playerRef.current.destroy();
      }
    };
  }, []);

  useEffect(() => {
    if (playerRef.current && playerRef.current.loadVideoById) {
      playerRef.current.loadVideoById(videoId);
    }
  }, [videoId]);

  return (
    <div>
      <div id="player"></div>
    </div>
  );
};

export default PlayerFrame;
;

export default PlayerFrame;

Edit: I changed the Code and now the player dont gets destroyd if I change the VideoID but still the same problem, when I switch to another Component the player gets destroyed and dont rerender if I open the Component again

import React, { useEffect, useRef } from 'react'
import PlayerFrame from './PlayerFrame';
...

interface State {
   ...
}

export default class Player extends React.Component<any, State> {
    constructor(props: any) {
        super(props);
        this.state = {
           ...
        }
    }
    
    componentDidMount() {
        this.getData();
    }

    handlePlayerStateChange = () => {
        this.setState((prevState) => ({
            playlist: prevState.playlist.slice(1),
        }));
    }
      

    public render() {
        const { isLoading, playlist, settingsGeneral } = this.state;
        const currentVideoID = playlist.length > 0 ? playlist[0].videoID : '';
    
        return (
          <>
            {isLoading ? (
              <div><Spin></div>
            ) : (
              <>
                <PlayerFrame
                    videoId={currentVideoID}
                    width={settingsGeneral.playerWidth.toString()}
                    height={settingsGeneral.playerHeight.toString()}
                    onStateChange={this.handlePlayerStateChange}
                />
              </>
            )}
          </>
        );
      }
...
}

2

Answers


  1. Here is a suggestion. It’s based on your code

    const PlayerFrame = ({ videoId }) => {
      const playerRef = useRef(null);
    
      useEffect(() => {
        const tag = document.createElement('script');
        tag.src = "https://www.youtube.com/iframe_api";
        const firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    
        const onYouTubeIframeAPIReady = () => {
          playerRef.current = new (window as any).YT.Player('player', {
            height: '1000',
            width: '1000',
            videoId: videoId,
            events: {
              onReady: (event) => {
                // Optionally do something when player is ready
              },
              onStateChange: (event) => {
                // Handle state changes if necessary
              }
            }
          });
        };
    
        (window as any).onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
    
        return () => {
          if (playerRef.current) {
            playerRef.current.destroy();
          }
          (window as any).onYouTubeIframeAPIReady = null;
        };
      }, [videoId]);
    
      return (
        <div>
          <div id="player"></div>
        </div>
      );
    };
    
    export default PlayerFrame;
    

    For this way,
    The playerRef using to store the player instance.
    On component unmount, the player instance is destroyed, which ensures that it cleans up.

    Hope it can help you.

    Login or Signup to reply.
  2. The way I see it, you first initialize it on this event window.onYouTubeIframeAPIReady which is not called on re-render. So I think you could just reinitialize the playerRef.current again after re-render, and trigger it by detecting if youtube-api script has been injected. So you can first give data-attribute to the script:

            tag.id = "youtube-api";
            tag.src = "https://www.youtube.com/iframe_api";
            tag.dataset.name = "youtube-api"; // add this
    

    Then you can reinitialize it this way:

        if (
          window.YT &&
          document.querySelector('script[data-name="youtube-api"]')
        ) {
          playerRef.current = new window.YT.Player("player", {
            height: 400,
            width: 400,
            videoId: videoId,
            events: {
              onReady: onPlayerReady,
            },
          });
        }
    

    Forked sandbox:

    Edit youtubeframe-forked-9hd7p3

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search