From 7eb0366b8d74825aae7871d478f86e5410d29608 Mon Sep 17 00:00:00 2001 From: TaduJR Date: Mon, 20 Apr 2026 11:32:04 +0300 Subject: [PATCH 1/2] fix(VideoPlayer): stop timeUpdate ticks when paused, memoize fakeReportID --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 11 +++++++++-- src/components/VideoPlayer/index.native.tsx | 5 +++-- src/components/VideoPlayer/index.tsx | 5 +++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index b42c3477ec13..2c12848f9efb 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -83,12 +83,19 @@ function BaseVideoPlayer({ useVideoPlayer(sourceURL, (player) => { player.loop = isLooping; player.muted = true; - player.timeUpdateEventInterval = 0.1; + player.timeUpdateEventInterval = 0; }), ); /* eslint-enable no-param-reassign */ - const isPlaying = videoPlayerRef.current.playing; + // `useEvent` — direct `.playing` read wouldn't re-render when play state changes. + const {isPlaying} = useEvent(videoPlayerRef.current, 'playingChange', {isPlaying: videoPlayerRef.current.playing, oldIsPlaying: false} as PlayingChangeEventPayload); + + // Keep in an effect — the web setter synchronously emits `timeUpdate`, so a render-time write re-enters `useEvent`. + useEffect(() => { + videoPlayerRef.current.timeUpdateEventInterval = isPlaying ? 0.1 : 0; + }, [isPlaying]); + const {currentTime, bufferedPosition} = useEvent(videoPlayerRef.current, 'timeUpdate', {currentTime: 0, bufferedPosition: 0} as TimeUpdateEventPayload); const {status} = useEvent(videoPlayerRef.current, 'statusChange', {status: shouldUseSharedVideoElement ? playerStatus.current : 'loading'} as StatusChangeEventPayload); diff --git a/src/components/VideoPlayer/index.native.tsx b/src/components/VideoPlayer/index.native.tsx index 602950bfdeaa..42f5b2a7c8a2 100644 --- a/src/components/VideoPlayer/index.native.tsx +++ b/src/components/VideoPlayer/index.native.tsx @@ -1,11 +1,12 @@ -import React from 'react'; +import React, {useState} from 'react'; import uniqueIDForVideoWithoutReport from '@components/VideoPlayerContexts/PlaybackContext/uniqueID'; import CONST from '@src/CONST'; import BaseVideoPlayer from './BaseVideoPlayer'; import type VideoPlayerProps from './types'; function VideoPlayer({videoControlsStyle, shouldUseControlsBottomMargin = true, ...props}: VideoPlayerProps) { - const {fakeReportID} = uniqueIDForVideoWithoutReport(); + // `fakeReportID` is a getter that increments each access — freeze it per instance. + const [fakeReportID] = useState(() => uniqueIDForVideoWithoutReport().fakeReportID); const {reportID} = props; return ( diff --git a/src/components/VideoPlayer/index.tsx b/src/components/VideoPlayer/index.tsx index d0c5a2eb9940..2ff53123f703 100644 --- a/src/components/VideoPlayer/index.tsx +++ b/src/components/VideoPlayer/index.tsx @@ -1,10 +1,11 @@ -import React from 'react'; +import React, {useState} from 'react'; import uniqueIDForVideoWithoutReport from '@components/VideoPlayerContexts/PlaybackContext/uniqueID'; import BaseVideoPlayer from './BaseVideoPlayer'; import type VideoPlayerProps from './types'; function VideoPlayer(props: VideoPlayerProps) { - const {fakeReportID} = uniqueIDForVideoWithoutReport(); + // `fakeReportID` is a getter that increments each access — freeze it per instance. + const [fakeReportID] = useState(() => uniqueIDForVideoWithoutReport().fakeReportID); const {reportID} = props; return ( From afd204fdfe2bc163e35ade47921f6fa28aaa5eab Mon Sep 17 00:00:00 2001 From: TaduJR Date: Mon, 20 Apr 2026 11:56:25 +0300 Subject: [PATCH 2/2] refactor(VideoPlayer): move interval toggle into event listener --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index 2c12848f9efb..e86e06c51b6f 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -91,11 +91,6 @@ function BaseVideoPlayer({ // `useEvent` — direct `.playing` read wouldn't re-render when play state changes. const {isPlaying} = useEvent(videoPlayerRef.current, 'playingChange', {isPlaying: videoPlayerRef.current.playing, oldIsPlaying: false} as PlayingChangeEventPayload); - // Keep in an effect — the web setter synchronously emits `timeUpdate`, so a render-time write re-enters `useEvent`. - useEffect(() => { - videoPlayerRef.current.timeUpdateEventInterval = isPlaying ? 0.1 : 0; - }, [isPlaying]); - const {currentTime, bufferedPosition} = useEvent(videoPlayerRef.current, 'timeUpdate', {currentTime: 0, bufferedPosition: 0} as TimeUpdateEventPayload); const {status} = useEvent(videoPlayerRef.current, 'statusChange', {status: shouldUseSharedVideoElement ? playerStatus.current : 'loading'} as StatusChangeEventPayload); @@ -292,6 +287,8 @@ function BaseVideoPlayer({ useEventListener(videoPlayerRef.current, 'playingChange', (payload: PlayingChangeEventPayload) => { const isVideoPlaying = payload.isPlaying; + // Toggled in the listener (not a `useEffect`) — the web setter synchronously emits `timeUpdate`, which would re-enter `useEvent` from render. + videoPlayerRef.current.timeUpdateEventInterval = isVideoPlaying ? 0.1 : 0; if (isVideoPlaying && isEnded) { setIsEnded(false); }