reactjshtml5-audioplaylisthowler.js

How to use ReactHowler with a playlist


I'm fetching a playlist from an external source as a JSON object, containing the title of a track, but also the URL to the audio file. I'm mapping this object to <li> elements withing an <ul>, but I can't get it to work. I installed Howler and HowlerReact, but on pageload I'm getting the following error:

This is my code:

import React, { useState } from 'react';
import ReactHowler from 'react-howler'
import Async from 'react-async';

const loadTracks = () =>
    fetch('...')
    .then(res => (res.ok ? res : Promise.reject(res)))
    .then(res => res.json());

const Music = () => {
    const [ play, setPlay ] = useState({
        currentTrack: null,
        play: false
    });

    return (
        <ul className="music">
            <ReactHowler src={currentTrack} playing={play} />
            <Async promiseFn={loadTracks}>
                <Async.Loading>Loading...</Async.Loading>
                <Async.Fulfilled>
                    {data => {
                        return (
                            Object.keys(data[title].tracks).map(track => {console.log(data[title].tracks);
                                return(
                                <li key={data[title].tracks[track].filename}>
                                    <button 
                                        data-permalink={data[title].tracks[track].title}
                                        onClick={() => setPlay({currentTrack: data[title].tracks[track].filename, play: !play})}>
                                        {data[title].tracks[track].title}
                                    </button>
                                </li>
                            )})
                        )
                    }}
                </Async.Fulfilled>
                <Async.Rejected>
                    {error => `Something went wrong: ${error.message}`}
                </Async.Rejected>
            </Async>
        </ul>
    );
}

export default Music;

I have yet to get familiar with React functional components, I understand the basics, but I don't know how to combine it with things such as an audio player.

I'm not particularly bound to Howler, so any suggestion on how to get this to work is most welcome.


Solution

  • I got it working with a plain HTML5 audio element, no Howler required:

    import React, { useState, useEffect, useRef } from 'react';
    import Async from 'react-async';
    
    function countDown(duration, time) {
        if (!isNaN(time)) {
            var timeLeft = duration - time;
            return (
                Math.floor(timeLeft / 60) + ":" + ("0" + Math.floor(timeLeft % 60)).slice(-2)
            );
        }
    }
    
    const loadTracks = () =>
        fetch('...')
        .then(res => (res.ok ? res : Promise.reject(res)))
        .then(res => res.json());
    
    const Music = () => {
        const player = useRef(null);
        const [ state, setState ] = useState({
            currentTrack: null,
            currentTime: null,
            duration: null
        });
        let currentTime = getTime(state.currentTime);
        let duration = getTime(state.duration);
        let timeLeft = countDown(state.duration, state.currentTime);
        let currentTrack = null;
    
        useEffect(() => {
            if (state.currentTrack) {
                player.current.src = state.currentTrack;
                player.current.play();
            }
            player.current.addEventListener('timeupdate', e => {
                setState(prevState => ({
                    currentTime: e.target.currentTime,
                    duration: e.target.duration,
                    currentTrack: prevState.currentTrack
                }));
            });
    
            return function cleanup() {
                player.current.removeEventListener('timeupdate', () => {});
            }
        }, [state.currentTrack]);
    
        return (
            <ul className="music">
                <Async promiseFn={loadTracks}>
                    <Async.Loading>Loading...</Async.Loading>
                    <Async.Fulfilled>
                        {data => {
                            return (
                                Object.keys(data[title].tracks).map(track => {console.log(data[title].tracks);
                                    return(
                                    <li key={data[title].tracks[track].filename}>
                                        <button 
                                            data-permalink={data[title].tracks[track].title}
                                            onClick={() => setState({currentTrack: data[title].tracks[track].filename})}>
                                            {data[title].tracks[track].title}
                                        </button>
                                    </li>
                                )})
                            )
                        }}
                    </Async.Fulfilled>
                    <Async.Rejected>
                        {error => `Something went wrong: ${error.message}`}
                    </Async.Rejected>
                </Async>
            </ul>
        );
    }
    
    export default Music;