'Svelte: Remount component to overwrite media elements
- Context
In my Svelte app, I have multiple pages, each showing one or multiple videos. For rendering the videos I reuse a video component (simplified):
// video component
<video poster="{source.thumb}">
<source type="{source.mime}" src="{source.source}" >
</video>
The main page receives the video content via an api and calls the video component:
// calling video component on main page
<script>
let source = {
thumb: 'thumb.jpg',
source: 'video.mp4',
mime: 'video/mp4',
};
</script>
<Video source={source} />
All works fine, the video is rendered and can be played.
- Problem
But: when I navigate or want to replace a video with another, the old video element somehow still exists and playback continues.
I could use beforeUpdate()
to pause the video. But then, the new video weirdly is loaded at the exact same playback time and everything gets mixed up. Or if I remove the video element in beforeUpdate()
, it doesn't get filled with the new information.
It kinda makes sense, because the video
media element stays the exact same thing while only attributes and content change. Thus the state and already buffered source remains.
I somehow would need to assure, that when the data changes, the video component must completely be remounted. Does anyone know how to do that? Thanks!
Solution 1:[1]
After some trial and error and with the help of @voscausa it now works:
<script>
export let source;
let renderVideo = true;
$: { reMountVideo( source.source ) }
function reMountVideo(){
renderVideo = false;
setTimeout(() => renderVideo = true, 0);
}
</script>
{#if renderVideo === true}
<video poster="{source.thumb}">
<source type="{source.mime}" src="{source.source}" >
</video>
{/if}
It checks if the video url changes, and if so, triggers reMountVideo()
.
Solution 2:[2]
I had the same kind of problem with the <input type="file" ....>
And solved it using:
let video = true:
function reMountVideo() {
video = false;
setTimeout(() => video = true, 0);
}
{#if video }
<Video source={source} />
{/if}
I did not try $destroy
Solution 3:[3]
Current version of svelte supports this. This attaches a callback that is called on video
every time the property src
changes.
<script>
let src = ...
function load(node, src){
if(src){
node.src = src;
node.load();
}
return {
update(src){
if(src){
node.src = src;
node.load();
}
}
}
}
</script>
<video use:load={src}>
Solution 4:[4]
To trigger a rerender of the video when the source changes, a {#key} block could be used REPL
<script>
export let source;
</script>
{#key source}
<video poster="{source.thumb}" controls>
<source type="{source.mime}" src="{source.source}" >
</video>
{/key}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | moritzebeling |
Solution 2 | |
Solution 3 | Nearoo |
Solution 4 | Corrl |