I can't figure out how to prevent the playerRef
from reloading when clipLink
change.
When i work on dev mode(npm run dev
), i can switch videos without reloading playerRef
. However, when i work on production mode(npm run build && npm start
), i can't switch videos without reloading playerRef
.
Please assume that there's a button in the Parent Component that changes the clipLink
when clicked(I removed it from the code in here for simplicity)
This is Parent Component
import { useRef } from "react";
import { YouTubePlayer } from 'react-youtube';
import ChildPage from "@/components/ChildPage";
const ParentPage = () => {
const playerRef = useRef<YouTubePlayer | null>(null);
return (
<>
<ChildPage
clipLink={clipLink}
playerRef={playerRef}
/>
</>
);
};
export default ParentPage;
This is Child Component
import { useEffect } from "react";
import YouTube, { YouTubeProps } from 'react-youtube';
const ChildPage = ({ clipLink, playerRef }) => {
const onPlayerReady: YouTubeProps['onReady'] = (event) => {
playerRef.current = event.target;
event.target.playVideo();
event.target.setVolume(30);
};
const onPlayerStateChange: YouTubeProps['onStateChange'] = (event) => {
const playerState = event.data;
switch (playerState) {
case 0:
event.target.playVideo();
break;
default:
console.log('Player state changed to:', playerState);
}
};
const opts: YouTubeProps['opts'] = {
playerVars: {
autoplay: 1,
controls: 1,
fs: 0,
modestbranding: 1,
rel: 0,
iv_load_policy: 3,
},
};
useEffect(() => {
if (clipLink && playerRef.current) {
try {
playerRef.current.loadVideoById(clipLink);
playerRef.current.playVideo();
playerRef.current.setVolume(30);
} catch (error) {
console.error('Error loading video -', error);
}
}
}, [clipLink]);
return (
<>
<YouTube
videoId={clipLink}
opts={opts}
onReady={onPlayerReady}
onStateChange={onPlayerStateChange}
/>
</>
);
};
export default ChildPage;
As you can see, in the Child component, I have playerRef.current.loadVideoById(clipLink);
.
I thought this code would change the video of playerRef
without reloading, and it really worked as intended in dev mode(npm run dev
).
However, in production mode(npm run build && npm start
), it doesn't work as expected...
The issue you're experiencing stems from how React handles component re-renders and the YouTube library. When the clipLink
changes, React unmounts and remounts the YouTube player in production mode, causing the playerRef
to reset. In development mode, React’s hot-reloading behaviour might suppress some re-mounting behaviours, which can explain the discrepancy.
To fix this, you must ensure the YouTube player remains mounted, even when the clipLink
changes. Instead of unmounting and remounting the player, you can update the video being played using the player's loadVideoById method.
import { useEffect } from "react";
import YouTube, { YouTubeProps } from 'react-youtube';
const ChildPage = ({ clipLink, playerRef }) => {
const onPlayerReady: YouTubeProps['onReady'] = (event) => {
playerRef.current = event.target;
event.target.playVideo();
event.target.setVolume(30);
};
const onPlayerStateChange: YouTubeProps['onStateChange'] = (event) => {
const playerState = event.data;
switch (playerState) {
case 0:
event.target.playVideo();
break;
default:
console.log('Player state changed to:', playerState);
}
};
const opts: YouTubeProps['opts'] = {
playerVars: {
autoplay: 1,
controls: 1,
fs: 0,
modestbranding: 1,
rel: 0,
iv_load_policy: 3,
},
};
// UseEffect to update the video when clipLink changes
useEffect(() => {
if (playerRef.current && clipLink) {
const videoId = new URLSearchParams(new URL(clipLink).search).get('v');
if (videoId) {
playerRef.current.loadVideoById(videoId);
}
}
}, [clipLink, playerRef]);
return (
<YouTube
videoId={new URLSearchParams(new URL(clipLink).search).get('v') || ""}
opts={opts}
onReady={onPlayerReady}
onStateChange={onPlayerStateChange}
/>
);
};
export default ChildPage;