Skip to content

Commit 4ca6a1b

Browse files
authored
fix: resolve vimeo player memory leak and unnecessary re-subscriptions (#28)
- Extract throttleMs from callbackOrThrottle to optimize useEffect dependencies - Add controller.off() cleanup logic when last listener is removed - Prevent memory leaks on component unmount
1 parent ce5c431 commit 4ca6a1b

File tree

6 files changed

+52
-5
lines changed

6 files changed

+52
-5
lines changed

.changeset/quiet-bees-happen.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"react-native-vimeo-bridge": patch
3+
---
4+
5+
fix: resolve vimeo player memory leak and unnecessary re-subscriptions
6+
7+
- Extract `throttleMs` from `callbackOrThrottle` to optimize `useEffect` dependencies
8+
- Add `controller.off()` cleanup logic when last listener is removed
9+
- Prevent memory leaks on component unmount

src/VimeoView.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,35 @@ function VimeoView({ player, height = 200, width = screenWidth, style, webViewPr
173173
getVideoWidth: () => player.getVideoWidth(),
174174
getVideoHeight: () => player.getVideoHeight(),
175175
getVideoUrl: () => player.getVideoUrl(),
176-
destroy: () => player.destroy(),
176+
destroy: () => {
177+
player.off('play');
178+
player.off('playing');
179+
player.off('pause');
180+
player.off('ended');
181+
player.off('timeupdate');
182+
player.off('progress');
183+
player.off('seeking');
184+
player.off('seeked');
185+
player.off('texttrackchange');
186+
player.off('chapterchange');
187+
player.off('cuechange');
188+
player.off('cuepoint');
189+
player.off('volumechange');
190+
player.off('playbackratechange');
191+
player.off('bufferstart');
192+
player.off('bufferend');
193+
player.off('error');
194+
player.off('loaded');
195+
player.off('durationchange');
196+
player.off('fullscreenchange');
197+
player.off('qualitychange');
198+
player.off('camerachange');
199+
player.off('resize');
200+
player.off('enterpictureinpicture');
201+
player.off('leavepictureinpicture');
202+
player.destroy();
203+
},
204+
off: (event) => player.off(event),
177205
}
178206
}
179207
})();

src/hooks/useVimeoEvent.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ function useVimeoEvent<T extends keyof VimeoPlayerEventMap>(
5353
deps?: React.DependencyList,
5454
): VimeoPlayerEventMap[T] | null | undefined {
5555
const isCallback = typeof callbackOrThrottle === 'function';
56+
const throttleMs = typeof callbackOrThrottle === 'number' ? callbackOrThrottle : undefined;
5657

5758
const callbackRef = useRef<EventCallback<VimeoPlayerEventMap[T]> | null>(isCallback ? callbackOrThrottle : null);
5859

@@ -73,8 +74,7 @@ function useVimeoEvent<T extends keyof VimeoPlayerEventMap>(
7374
}
7475

7576
if (!isCallback) {
76-
if (eventType === 'timeupdate' && typeof callbackOrThrottle === 'number') {
77-
const throttleMs = callbackOrThrottle;
77+
if (eventType === 'timeupdate' && throttleMs) {
7878
const now = Date.now();
7979
if (now - lastUpdateRef.current < throttleMs) {
8080
return;
@@ -87,7 +87,7 @@ function useVimeoEvent<T extends keyof VimeoPlayerEventMap>(
8787
});
8888

8989
return unsubscribe;
90-
}, [player, eventType, isCallback, callbackOrThrottle]);
90+
}, [player, eventType, isCallback, throttleMs]);
9191

9292
return isCallback ? undefined : data;
9393
}

src/module/VimeoPlayer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class VimeoPlayer {
4646
eventSet?.delete(callback);
4747

4848
if (eventSet?.size === 0) {
49+
this.controller?.off(eventType);
4950
this.listeners.delete(eventType);
5051
}
5152
};

src/module/WebVimeoPlayerController.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { EmbedOptions, VimeoPlayer } from '../types/vimeo';
1+
import type { EmbedOptions, EventCallback, VimeoPlayer } from '../types/vimeo';
22

33
class WebVimeoPlayerController {
44
private player: VimeoPlayer | null = null;
@@ -152,6 +152,10 @@ class WebVimeoPlayerController {
152152
return this.player?.getVideoUrl() ?? '';
153153
}
154154

155+
off(event: string, callback?: EventCallback): void {
156+
this.player?.off(event, callback);
157+
}
158+
155159
dispose(): void {
156160
if (this.player) {
157161
try {

src/module/WebviewVimeoPlayerController.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type WebView from 'react-native-webview';
2+
import type { EventCallback } from '../types/vimeo';
23

34
class WebviewVimeoPlayerController {
45
private webViewRef: React.RefObject<WebView | null>;
@@ -96,6 +97,10 @@ class WebviewVimeoPlayerController {
9697
await this.executeCommand('destroy');
9798
}
9899

100+
async off(event: string, _callback?: EventCallback): Promise<void> {
101+
await this.executeCommand('off', [event]);
102+
}
103+
99104
private executeCommand(
100105
command: string,
101106
args: (string | number | boolean | undefined)[] = [],

0 commit comments

Comments
 (0)