Marco Mazzai
Marco Mazzai

Reputation: 67

React hooks music player onClick plays always the same song

I'm trying to code a Music Player with React Hooks. This music player fetches the first five most famous songs of a band through the Deezer API.

When I click play/pause on any of the song it always plays the same song.

I know the problem is with useRef but I can't figure it out how to fix it. I read many tutorials and posts but nothing address this situation. Can somebody help? Thanks!

import React, { useState, useEffect } from 'react';
import './App.scss';

const BASE_URL = 'https://cors-anywhere.herokuapp.com/https://api.deezer.com/artist/182/top'

function MusicPlayer(){
  const [ songs, setSongs ] = useState([])
  const [ isLoading, setIsLoading ] = useState(false)
  const [ error, setError ] = useState(null)

  const inputRef = React.useRef()

  useEffect(() => {
    setIsLoading(true)
    fetch(BASE_URL, {headers: {
      "Accept": "application/json",
      "Access-Control-Allow-Origin": "*"
    }})
    .then (res => {
      return res.ok ? res.json() : throw new Error("Mistake!")
    })
    .then(songs => {
      setSongs(songs.data)
      setIsLoading(false)
    })
    .catch(error => {
      setError(error)
    })
    }, [])

  if (error){ return <p> { error.message }</p> }
  if (isLoading ){ return <p> Loading songs...</p> }

  return(
    <div>
      { songs.map( (song, i) => (
          <div key={i}>
            <h1>{song.title}</h1>
            <img src={song.contributors[0].picture_small}/><br/>
            <audio ref={inputRef} src={song.preview} />
            <button onClick={() => inputRef.current.play()}>Play</button>          
            <button onClick={() => inputRef.current.pause()}>Pause</button>
          </div>
          ))
      }
    </div>
    )
    }

export default MusicPlayer

Upvotes: 1

Views: 1098

Answers (1)

Colin Ricardo
Colin Ricardo

Reputation: 17259

This should do what you want:

import ReactDOM from "react-dom";
import React, { useState, useEffect } from "react";

const BASE_URL =
  "https://cors-anywhere.herokuapp.com/https://api.deezer.com/artist/182/top";

function MusicPlayer() {
  const [songs, setSongs] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [currentSong, setCurrentSong] = useState(null);
  const inputRef = React.useRef();

  useEffect(() => {
    if (currentSong) {
      inputRef.current.play();
    }
    console.log(currentSong);
  }, [currentSong]);

  useEffect(() => {
    setIsLoading(true);
    fetch(BASE_URL, {
      headers: {
        Accept: "application/json",
        "Access-Control-Allow-Origin": "*"
      }
    })
      .then(res => {
        return res.ok ? res.json() : null;
      })
      .then(songs => {
        setSongs(songs.data);
        setIsLoading(false);
      })
      .catch(error => {
        setError(error);
      });
  }, []);

  if (error) {
    return <p> {error.message}</p>;
  }
  if (isLoading) {
    return <p> Loading songs...</p>;
  }

  const handlePlay = songPreview => {
    if (currentSong) {
      if (currentSong === songPreview) {
        return inputRef.current.play();
      }
    }
    setCurrentSong(songPreview);
  };

  return (
    <div>
      {songs.map((song, i) => (
        <div key={i}>
          <h1>{song.title}</h1>
          <img src={song.contributors[0].picture_small} />
          <br />
          <audio ref={inputRef} src={currentSong} />
          <button onClick={() => handlePlay(song.preview)}>Play</button>
          <button onClick={() => inputRef.current.pause()}>Pause</button>
        </div>
      ))}
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<MusicPlayer />, rootElement);

Basically, you need to keep track of what song is currently playing.

CodeSandbox demo here.

Upvotes: 3

Related Questions