Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- Upgraded example app to React-Native v0.84.1.

### Fixed

- Fixed an issue on Web where Google IMA could fail to initialize when used with `react-reverse-portal` due to the player container not being attached to the document yet.

## [10.13.0] - 26-03-27

### Fixed
Expand Down
10 changes: 6 additions & 4 deletions src/internal/THEOplayerView.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import React, { useEffect, useRef } from 'react';
import type { THEOplayerViewProps } from 'react-native-theoplayer';
import { ChromelessPlayer } from 'theoplayer';
import { THEOplayerWebAdapter } from './adapter/THEOplayerWebAdapter';
import { useIsAttached } from './hooks/useIsAttached';

export function THEOplayerView(props: React.PropsWithChildren<THEOplayerViewProps>) {
const { config, children, onPlayerReady, onPlayerDestroy } = props;
const player = useRef<ChromelessPlayer | null>(null);
const adapter = useRef<THEOplayerWebAdapter | null>(null);
const container = useRef<null | HTMLDivElement>(null);
const container = useRef<HTMLDivElement | null>(null);
const attachedToDocument = useIsAttached(container);

useEffect(() => {
// Create player inside container.
if (container.current) {
// Create player inside container once it is attached to the document.
if (container.current && attachedToDocument) {
const ads = {
...config?.ads,
googleIma: {
Expand Down Expand Up @@ -51,7 +53,7 @@ export function THEOplayerView(props: React.PropsWithChildren<THEOplayerViewProp
};
// TODO: Follow the rules of react hooks, to be fixed in next major because it's a breaking change for some customers.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [container]);
}, [container, attachedToDocument]);

return (
// Note: `display: contents` causes an element's children to appear as if they were direct children of the element's parent,
Expand Down
44 changes: 44 additions & 0 deletions src/internal/hooks/useIsAttached.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* React hook to determine if a referenced HTMLDivElement is currently attached to the document.
*
* This hook checks if the provided ref's current element is attached to the DOM (document.body).
* Returns a boolean indicating the attachment state.
* Once attached, it will stop observing for changes to optimize performance.
*
* @param ref - React ref object pointing to an HTMLDivElement
* @returns boolean - true if the element is attached to the document, false otherwise
*/
import { RefObject, useEffect, useState } from 'react';

/**
* @param ref
*/
export const useIsAttached = (ref: RefObject<HTMLDivElement | null>) => {
const [isAttached, setIsAttached] = useState<boolean>(false);

useEffect(() => {
if (!ref.current) return;

const checkAttached = () => {
const connected = ref.current?.isConnected ?? false;
setIsAttached(connected);
return connected;
};

if (checkAttached()) return;

const observer: MutationObserver = new MutationObserver(() => {
const attached = checkAttached();
// Once attached, stop observing.
if (attached) {
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
return () => observer.disconnect();
}, [ref]);
return isAttached;
};
Loading