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
Here is a suggestion. It’s based on your code
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.
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 theplayerRef.current
again after re-render, and trigger it by detecting ifyoutube-api
script has been injected. So you can first give data-attribute to the script:Then you can reinitialize it this way:
Forked sandbox: