Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manage Multiple Audio Sources in React

I have a React component that plays/pauses audio when you click on a button. It works great and I render about 5 of these on a page at once. However, if you click play on one, and then click play on another, both audio's are playing, which isn't great. Here's my code for the component:

import React from 'react';
import playIcon from './images/play.png';
import pauseIcon from './images/pause.png';
class Music extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 'play': false };
        this.url = props.src;
        this.audio = new Audio(this.url);
        this.audio.preload = 'none';
        this.togglePlay = this.togglePlay.bind(this);
    }

    togglePlay() {
        this.setState({'play': !this.state.play}, () => {
            this.state.play ? this.audio.play() : this.audio.pause();
        });
    }

    componentWillUnmount () {
        this.audio.pause();
    }

    render() {
        return (
            <div style={this.props.style} className={this.props.className}>
                {this.state.play
                    ? <button className="audio-button" aria-label="Pause" onClick={this.togglePlay}><img src={pauseIcon} width="34" height="34" alt="Pause"></img></button>
                    : <button className="audio-button" aria-label="Play" onClick={this.togglePlay}><img src={playIcon} width="34" height="34" alt="Play"></img></button>}
            </div>
        );
    }
}

export default Music;

I've done some looking around and a potential solution is to use redux or some other state management library. I'd like to avoid this as I otherwise have no need for that in this site. I've looked into event listeners (namely the solution proposed here) but when I do a document.getElementsByTagName('audio') I get back an empty HTMLCollection. This solution is closer, but I can't bridge the gap between it's implementation in jQuery to the one I'm using in React.

Is there a way to identify the playing audio and pause it from a React Component before playing new audio? Any and all suggestions are appreciated.

like image 771
quicklikerabbit Avatar asked May 30 '18 01:05

quicklikerabbit


1 Answers

import React from 'react';

import audio1 from './audio1.mp3';
import audio2 from './audio2.mp3';

class AudioList extends React.Component {
    constructor (props) {
        super(props);

        this.audios = props.list.map(audio => new Audio(audio));
    }

    getCurrentAudio () {
        return this.audios.find(audio => false === audio.paused);
    }

    toggle (nextAudio) {
        const currentAudio = this.getCurrentAudio();

        if (currentAudio && currentAudio !== nextAudio) {
            currentAudio.pause();
        }

        nextAudio.paused ? nextAudio.play() : nextAudio.pause();
    }

    render () {
        return (
            <div>
                { this.audios.map((audio, index) =>
                    <button onClick={() => this.toggle(audio) }>
                        PLAY AUDIO { index }
                    </button>
                ) }
            </div>
        )
    }
}


export default () => <AudioList list={[ audio1, audio2 ]} />;

PS: new Audio(url) returns a HTMLAudioElement.

like image 80
Isaac Ferreira Avatar answered Sep 30 '22 04:09

Isaac Ferreira