Free AI web copilot to create summaries, insights and extended knowledge, download it at here
7656
Abstract
<span class="hljs-keyword">const</span> <span class="hljs-title function_">playSideEffectCallback</span> = (<span class="hljs-params"></span>) => {
globalPlayMediaManager.<span class="hljs-title function_">playPlayer</span>(playerIdRef.<span class="hljs-property">current</span>);
};
<span class="hljs-keyword">return</span> { playSideEffectCallback };
};</pre></div><p id="e479">Please note that this hook returns a function <code>playSideEffectCallback</code> that needs to be triggered when the media player is about to start playing. This callback will inform any other active players to stop playing.</p><p id="82b2">The final step is to integrate this hook with any media player in this way:</p><div id="3730"><pre><span class="hljs-keyword">const</span> <span class="hljs-title function_">VideoPlayer</span> = (<span class="hljs-params">props: VideoHTMLAttributes<HTMLVideoElement></span>) => {
<span class="hljs-keyword">const</span> playerRef = useRef<<span class="hljs-title class_">HTMLVideoElement</span>>(<span class="hljs-literal">null</span>);
<span class="hljs-keyword">const</span> { playSideEffectCallback } = <span class="hljs-title function_">useSinglePlayerPlaying</span>({
<span class="hljs-attr">stop</span>: <span class="hljs-function">() =></span> {
<span class="hljs-keyword">if</span> (playerRef.<span class="hljs-property">current</span>) {
playerRef.<span class="hljs-property">current</span>.<span class="hljs-title function_">pause</span>();
}
}
});
<span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {
<span class="hljs-keyword">if</span> (playerRef.<span class="hljs-property">current</span>) {
playerRef.<span class="hljs-property">current</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">"playing"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">playSideEffectCallback</span>();
});
}
}, []);
<span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">video</span> {<span class="hljs-attr">...props</span>} <span class="hljs-attr">ref</span>=<span class="hljs-string">{playerRef}</span> /></span></span>;
};</pre></div><p id="7c37">Initially, we obtain a reference to a video HTML element, which grants us access to the player API. We need to pass <code>stop</code> method in <code>useSinglePlayerPlaying</code> hook and invoke <code>playSideEffectCallback</code> in the listener of the player when the video starts playing. That is it. Feel free to experiment with this implementation in the sandbox below. Try playing the first video and then the second one without pausing the first.</p>
<figure id="052a">
<div>
<div>
<img class="ratio" src="http://placehold.it/16x9">
<iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodesandbox.io%2Fembed%2Fsingle-video-playing-react-hooks-ukg8o9&display_name=CodeSandbox&url=https%3A%2F%2Fcodesandbox.io%2Fs%2Fukg8o9&image=https%3A%2F%2Fcodesandbox.io%2Fapi%2Fv1%2Fsandboxes%2Fukg8o9%2Fscreenshot.png&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codesandbox" allowfullscreen="" frameborder="0" height="500" width="1000">
</div>
</div>
</figure></iframe></div></div></figure><p id="d88d">You can also check out the entire code on CodeSandbox.</p><h1 id="5d93">2. useInsideScreenPlayerPlaying</h1><p id="a623">Here we should know <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API"><i>Intersection Observer API</i></a>, but in general, the approach will be the same. The difference only is that we should pass as arguments to a new hook two methods that manage <code>stop/play</code> of player and of course a reference to the HTML element where the video is inside.</p><p id="021c">Firstly, let’s implement the Subject interface which requires two methods for registering and unregistering video players. Additionally, we need to create a private field <code>observer</code>and initialize an instance of <i>IntersectionObserver</i>. This instance will be responsible for subscribing and unsubscribing HTML elements that are being monitored for their position on the screen. It’s important to note that when a player is removed from the React Tree, it must also be unsubscribed from the <i>IntersectionObserver</i>.</p><div id="e282"><pre><span class="hljs-keyword">type</span> <span class="hljs-title class_">PlayerItem</span> = {
<span class="hljs-attr">id</span>: <span class="hljs-built_in">string</span>;
<span class="hljs-attr">playerContainer</span>: <span class="hljs-title class_">Element</span>;
<span class="hljs-attr">controls</span>: { <span class="hljs-attr">stop</span>: <span class="hljs-function">() =></span> <span class="hljs-built_in">void</span>; <span class="hljs-attr">play</span>: <span class="hljs-function">() =></span> <span class="hljs-built_in">void</span> };
};
<span class="hljs-keyword">class</span> <span class="hljs-title class_">GlobalInsideScreenPlayerPlayingManager</span> {
<span class="hljs-attr">players</span>: <span class="hljs-title class_">Array</span><<span class="hljs-title class_">PlayerItem</span>> = [];
observer = <span class="hljs-keyword">new</span> <span class="hljs-title class_">IntersectionObserver</span>(<span class="hljs-function">(<span class="hljs-params">entries</span>) =></span> {
<span class="hljs-comment">// we will define implementation later</span>
});
<span class="hljs-title function_">registerPlayer</span>(<span class="hljs-params">player: PlayerItem</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">players</span>.<span class="hljs-title function_">push</span>(player);
}
<span class="hljs-title function_">removePlayer</span>(<span class="hljs-params">id: <span class="hljs-built_in">string</span></span>) {
<span class="hljs-keyword">const</span> removingPlayer = <span class="hljs-variable language_">this</span>.<span class="hljs-property">players</span>.<span class="hljs-title function_">find</span>(<span class="hljs-function">(<span class="hljs-params">player</span>) =></span> player.<span class="hljs-property">id</span> === id);
<span class="hljs-keyword">if</span> (removingPlayer && removingPlayer.<span class="hljs-property">playerContainer</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">observer</span>.<span class="hljs-title function_">unobserve</span>(removingPlayer.<span class="hljs-property">playerContainer</span>);
}
<span class="hljs-variable language_">this</span>.<span class="hljs-property">players</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">players</span>.<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">player</span>) =></span> player.<span class="hljs-property">id</span> === id);
}
}</pre></div><p id="c7d5">We also should implement a method <code>notify</code> in <i>Observer Pattern</i>, but the name convention for this method will be the same as we did in Chapter 1 — <code>playPlayer</code>. In this method, we should keep the currently active player in the private field of class <code>activePlayer</code>:</p><div id="b405"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">Gl
Options
obalInsideScreenPlayerPlayingManager</span> {
<span class="hljs-comment">// code is skipped because of brevity</span>
<span class="hljs-attr">activePlayer</span>: <span class="hljs-title class_">PlayerItem</span> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
<span class="hljs-title function_">playPlayer</span>(<span class="hljs-params">id: <span class="hljs-built_in">string</span></span>) {
<span class="hljs-keyword">const</span> activePlayer = <span class="hljs-variable language_">this</span>.<span class="hljs-property">players</span>.<span class="hljs-title function_">find</span>(<span class="hljs-function">(<span class="hljs-params">player</span>) =></span> player.<span class="hljs-property">id</span> === id);
<span class="hljs-keyword">if</span> (activePlayer) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">activePlayer</span> = activePlayer;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">observer</span>.<span class="hljs-title function_">observe</span>(activePlayer.<span class="hljs-property">playerContainer</span>);
}
}
}</pre></div><p id="61c5">Let’s implement a handler for <i>IntersectionObserver</i> that will determine whether to pause or continue playing a video based on the position of the player on the screen. If the player is on the screen, it should continue playing, but if it is outside the screen, it should be stopped:</p><div id="f3ae"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">GlobalInsideScreenPlayerPlayingManager</span> {
observer = new IntersectionObserver((entries) => {
<span class="hljs-keyword">if</span> (
<span class="hljs-keyword">this</span>.activePlayer &&
entries[<span class="hljs-number">0</span>].target === <span class="hljs-keyword">this</span>.activePlayer.playerContainer
) {
<span class="hljs-keyword">if</span> (entries[<span class="hljs-number">0</span>].isIntersecting) {
<span class="hljs-keyword">this</span>.activePlayer.controls.play();
}
<span class="hljs-keyword">if</span> (!entries[<span class="hljs-number">0</span>].isIntersecting) {
<span class="hljs-keyword">this</span>.activePlayer.controls.stop();
}
}
});
}</pre></div><p id="ee91">It’s time to create our hook, and approach absolutely the same as we did in chapter 1:</p><div id="2e7f"><pre><span class="hljs-keyword">const</span> globalInsideScreenPlayerPlayingManager = <span class="hljs-keyword">new</span> <span class="hljs-title class_">GlobalInsideScreenPlayerPlayingManager</span>();
<span class="hljs-keyword">const</span> <span class="hljs-title function_">useInsideScreenPlayerPlaying</span> = (<span class="hljs-params">{
playerContainerRef,
controls
}: {
playerContainerRef: RefObject<Element | <span class="hljs-literal">null</span>>;
controls: {
play: () => <span class="hljs-keyword">void</span>;
stop: () => <span class="hljs-keyword">void</span>;
};
}</span>) => {
<span class="hljs-keyword">const</span> playerIdRef = <span class="hljs-title function_">useRef</span>(<span class="hljs-title function_">uuid</span>());
<span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {
<span class="hljs-keyword">if</span> (playerContainerRef?.<span class="hljs-property">current</span>) {
globalInsideScreenPlayerPlayingManager.<span class="hljs-title function_">registerPlayer</span>({
<span class="hljs-attr">id</span>: playerIdRef.<span class="hljs-property">current</span>,
<span class="hljs-attr">playerContainer</span>: playerContainerRef.<span class="hljs-property">current</span>,
controls
});
}
<span class="hljs-keyword">return</span> <span class="hljs-function">() =></span>
globalInsideScreenPlayerPlayingManager.<span class="hljs-title function_">removePlayer</span>(playerIdRef.<span class="hljs-property">current</span>);
}, []);
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">playSideEffect</span>: <span class="hljs-function">() =></span>
globalInsideScreenPlayerPlayingManager.<span class="hljs-title function_">playPlayer</span>(playerIdRef.<span class="hljs-property">current</span>)
};
};</pre></div><p id="80b1">Let’s integrate this hook with out custom player:</p><div id="d7bb"><pre><span class="hljs-keyword">const</span> <span class="hljs-title function_">VideoPlayer</span> = (<span class="hljs-params">props: VideoHTMLAttributes<HTMLVideoElement></span>) => {
<span class="hljs-keyword">const</span> playerRef = useRef<<span class="hljs-title class_">HTMLVideoElement</span>>(<span class="hljs-literal">null</span>);
<span class="hljs-keyword">const</span> { playSideEffect } = <span class="hljs-title function_">useInsideScreenPlayerPlaying</span>({
<span class="hljs-attr">playerContainerRef</span>: playerRef,
<span class="hljs-attr">controls</span>: {
<span class="hljs-attr">play</span>: <span class="hljs-function">() =></span> {
<span class="hljs-keyword">if</span> (playerRef.<span class="hljs-property">current</span>) {
playerRef.<span class="hljs-property">current</span>.<span class="hljs-title function_">play</span>();
}
},
<span class="hljs-attr">stop</span>: <span class="hljs-function">() =></span> {
<span class="hljs-keyword">if</span> (playerRef.<span class="hljs-property">current</span>) {
playerRef.<span class="hljs-property">current</span>.<span class="hljs-title function_">pause</span>();
}
}
}
});
<span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {
<span class="hljs-keyword">if</span> (playerRef.<span class="hljs-property">current</span>) {
playerRef.<span class="hljs-property">current</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">"playing"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">playSideEffect</span>();
});
}
}, []);
<span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">video</span> {<span class="hljs-attr">...props</span>} <span class="hljs-attr">ref</span>=<span class="hljs-string">{playerRef}</span> /></span></span>;
};</pre></div><p id="aa55">Here you may play around with CodeSandbox of this implementation. Try to start playing any video and scroll down/up and see what will happen.</p>
<figure id="8538">
<div>
<div>
<img class="ratio" src="http://placehold.it/16x9">
<iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodesandbox.io%2Fembed%2Funmute-outside-video-un8cck%3Ffile%3D%2Fsrc%2Fcomponents%2FVideo.tsx%3A0-853&display_name=CodeSandbox&url=https%3A%2F%2Fcodesandbox.io%2Fs%2Fun8cck&image=https%3A%2F%2Fcodesandbox.io%2Fapi%2Fv1%2Fsandboxes%2Fun8cck%2Fscreenshot.png&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codesandbox" allowfullscreen="" frameborder="0" height="500" width="1000">
</div>
</div>
</figure></iframe></div></div></figure><p id="9ab1">You can view the complete code in CodeSandbox as well.</p><p id="f000">And that’s a wrap! Thank you for taking the time to read this article. I would greatly appreciate any feedback you may have.</p></article></body>