2

I have this Svelte code:

Player.svelte:

<script>
    import PlayerControls from './PlayerControls.svelte';

    let audio;
</script>

<audio src={...} bind:this={audio} />
<PlayerControls {audio} />

PlayerControls.svelte:

<script>
    import PlayIcon from '../icons/PlayIcon.svelte';
    import PauseIcon from '../icons/PauseIcon.svelte';

    export let audio;

    const onClick = () => {
        if (audio?.paused) audio.play();
        else audio.pause();
    };
</script>

{#if audio?.paused}
    <PlayIcon {onClick} />
{:else}
    <PauseIcon {onClick} />
{/if}

If I press the play icon, the icon doesn't change, but the audio starts, and if I click again, the audio stops, and the icon is unchanged. Seems like audio.paused "changes" only in the script, but not in the html. What is wrong here, and what I'm not understanding here about Svelte?

2
  • see: Reactivity / Updating arrays and objects '...Methods that mutate arrays or objects will not trigger updates by themselves.' Commented Dec 25, 2022 at 11:14
  • I would also check that the the play() and pause() methods actually update the pause property. Commented Dec 25, 2022 at 11:24

1 Answer 1

3

The most robust solution in your given situation is to leverage the specific <audio> element events to alert svelte to reinspect the state of the ref. This allows svelte to manage your listener lifecycle, and also allows the element itself to handle all the edge cases of when/how the state of playback changed.

<script>
  import { onMount, onDestroy } from 'svelte';
  import PlayerControls from './PlayerControls.svelte';

  let audio;

  const audioUpdated = (e) => {
    audio = audio;
  };
</script>

<audio
  bind:this={audio}
  src="https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3"
  on:pause={audioUpdated}
  on:play={audioUpdated}
  on:ended={audioUpdated}
/>

<PlayerControls {audio} />

REPL

Original Answer

A straightforward solution to your problem is covered in the link to the documentation in my comment Reactivity / Updating arrays and objects and is to simply reassign audio as the last step in your onClick handler.

Keep in mind that this doesn't track the change in paused if the audio finishes on its own.

const onClick = () => {
    if (audio?.paused) audio.play();
    else audio.pause();
    
    audio = audio;
};

REPL

Edit

I initially recommended that one could mutate the paused property directly:

const onClick = () => {
  if (audio?.paused) {
    audio.play();
    audio.paused = false;
  } else {
    audio.pause();
    audio.paused = true;
  }
};

But neglected the fact that audio is a ref and paused is a readonly property.

Sign up to request clarification or add additional context in comments.

1 Comment

I had read about how to update arrays and objects, but kinda forgot about it. Reassigning the audio make my code work, but assigning directly the paused property didn't work. I got my answer, I'll be more careful about arrays and objects state, thank you! This is the first time I'm trying to build something with Svelte, so I'm a bit sloppy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.