1+ import { EventEmitter } from 'events' ;
12import React , {
2- useRef ,
3- useState ,
4- useEffect ,
53 forwardRef ,
64 useCallback ,
5+ useEffect ,
76 useImperativeHandle ,
87 useMemo ,
8+ useRef ,
9+ useState ,
910} from 'react' ;
10- import { View , StyleSheet , Platform } from 'react-native' ;
11- import { WebView } from './WebView' ;
11+ import { Platform , StyleSheet , View } from 'react-native' ;
1212import {
13+ CUSTOM_USER_AGENT ,
14+ DEFAULT_BASE_URL ,
1315 PLAYER_ERROR ,
1416 PLAYER_STATES ,
15- DEFAULT_BASE_URL ,
16- CUSTOM_USER_AGENT ,
1717} from './constants' ;
18- import { EventEmitter } from 'events' ;
1918import {
20- playMode ,
21- soundMode ,
2219 MAIN_SCRIPT ,
2320 PLAYER_FUNCTIONS ,
21+ playMode ,
22+ soundMode ,
2423} from './PlayerScripts' ;
24+ import { WebView } from './WebView' ;
25+
26+ const deepComparePlayList = ( lastPlayList , playList ) => {
27+ return (
28+ typeof lastPlayList === typeof playList &&
29+ ( Array . isArray ( lastPlayList )
30+ ? lastPlayList . join ( '' )
31+ : lastPlayList === Array . isArray ( playList )
32+ ? playList . join ( '' )
33+ : playList )
34+ ) ;
35+ } ;
2536
2637const YoutubeIframe = ( props , ref ) => {
2738 const {
@@ -41,7 +52,7 @@ const YoutubeIframe = (props, ref) => {
4152 onError = _err => { } ,
4253 onReady = _event => { } ,
4354 playListStartIndex = 0 ,
44- initialPlayerParams = { } ,
55+ initialPlayerParams,
4556 allowWebViewZoom = false ,
4657 forceAndroidAutoplay = false ,
4758 onChangeState = _event => { } ,
@@ -50,6 +61,10 @@ const YoutubeIframe = (props, ref) => {
5061 onPlaybackRateChange = _playbackRate => { } ,
5162 } = props ;
5263
64+ const lastVideoIdRef = useRef ( videoId ) ;
65+ const lastPlayListRef = useRef ( playList ) ;
66+ const initialPlayerParamsRef = useRef ( initialPlayerParams || { } ) ;
67+
5368 const webViewRef = useRef ( null ) ;
5469 const eventEmitter = useRef ( new EventEmitter ( ) ) ;
5570 const [ playerReady , setPlayerReady ] = useState ( 0 ) ;
@@ -127,38 +142,37 @@ const YoutubeIframe = (props, ref) => {
127142 } , [ play , playerReady , mute , volume , playbackRate ] ) ;
128143
129144 useEffect ( ( ) => {
130- if ( playerReady < 1 ) {
145+ if ( playerReady < 1 || lastVideoIdRef . current === videoId ) {
131146 // no instance of player is ready
147+ // or videoId has not changed
132148 return ;
133149 }
134150
151+ lastVideoIdRef . current = videoId ;
152+
135153 webViewRef . current . injectJavaScript (
136154 PLAYER_FUNCTIONS . loadVideoById ( videoId , play ) ,
137155 ) ;
138- // We do not need `play` prop because we should not
139- // recall the load function when the prop changes
140- // eslint-disable-next-line react-hooks/exhaustive-deps
141- } , [ videoId , playerReady ] ) ;
156+ } , [ videoId , play , playerReady ] ) ;
142157
143158 useEffect ( ( ) => {
144159 if ( playerReady < 1 ) {
145160 // no instance of player is ready
146161 return ;
147162 }
148163
149- if ( ! playList ) {
164+ // Also, right now, we are helping users by doing "deep" comparisons of playList prop,
165+ // but in the next major we should leave the responsibility to user (either via useMemo or moving the array outside)
166+ if ( ! playList || deepComparePlayList ( lastPlayListRef . current , playList ) ) {
150167 return ;
151168 }
152169
170+ lastPlayListRef . current = playList ;
171+
153172 webViewRef . current . injectJavaScript (
154173 PLAYER_FUNCTIONS . loadPlaylist ( playList , playListStartIndex , play ) ,
155174 ) ;
156- // We do not need `play` and `playListStartIndex` props because we should not
157- // recall the load function when the props changes
158- // Also, right now, we are helping users by doing "deep" comparisons of playList prop,
159- // but in the next major we should leave the responsibility to user (either via useMemo or moving the array outside)
160- // eslint-disable-next-line react-hooks/exhaustive-deps
161- } , [ Array . isArray ( playList ) ? playList . join ( '' ) : playList , playerReady ] ) ;
175+ } , [ playList , play , playListStartIndex , playerReady ] ) ;
162176
163177 const onWebMessage = useCallback (
164178 event => {
@@ -222,9 +236,9 @@ const YoutubeIframe = (props, ref) => {
222236
223237 const source = useMemo ( ( ) => {
224238 const ytScript = MAIN_SCRIPT (
225- videoId ,
226- playList ,
227- initialPlayerParams ,
239+ lastVideoIdRef . current ,
240+ lastPlayListRef . current ,
241+ initialPlayerParamsRef . current ,
228242 allowWebViewZoom ,
229243 contentScale ,
230244 ) ;
@@ -241,9 +255,6 @@ const YoutubeIframe = (props, ref) => {
241255 const data = ytScript . urlEncodedJSON ;
242256
243257 return { uri : base + '?data=' + data } ;
244- // videoId, playlist, initialPlayerParams are only used once when initializing YT.Player instance
245- // further changes are handled by injectJavaScript
246- // eslint-disable-next-line react-hooks/exhaustive-deps
247258 } , [ useLocalHTML , contentScale , baseUrlOverride , allowWebViewZoom ] ) ;
248259
249260 return (
@@ -255,7 +266,9 @@ const YoutubeIframe = (props, ref) => {
255266 style = { [ styles . webView , webViewStyle ] }
256267 mediaPlaybackRequiresUserAction = { false }
257268 onShouldStartLoadWithRequest = { onShouldStartLoadWithRequest }
258- allowsFullscreenVideo = { ! initialPlayerParams ?. preventFullScreen }
269+ allowsFullscreenVideo = {
270+ ! initialPlayerParamsRef . current . preventFullScreen
271+ }
259272 userAgent = {
260273 forceAndroidAutoplay
261274 ? Platform . select ( { android : CUSTOM_USER_AGENT , ios : '' } )
0 commit comments