Search code examples
reactjsreact-hookshtml5-audio

How to toggle audio in React functional component


I am trying to play/stop an audio when clicking an <img />. Each <img /> is related to a different audio file. The audio should loop while the user do not stop it. I prefer to not use classes components because I could use hooks.

My actual code do not stop the audio; do not loop it; and if I click twice the audio is overlaped (two sounds at the same time).

import myJSON from "./assets/myJSON.json";

export default function Card() {
 
    const handleClick = (argument) => {
        new Audio(argument.target.dataset.sound).play()
    }
    
    return (
        <>
            {myJSON.map((myObject, index) => {
                return (
                    <img
                        src={myObject.image}
                        alt={myObject.name}
                        key={index}
                        onClick={handleClick} 
                        data-sound={myObject.sound}
                    />
                );
            })}
        </>
    );
}

I am free to change the json or try a different approach.


Solution

  • I have created a code sandbox for you that you can access here: https://codesandbox.io/s/silly-hypatia-8fwun?file=/src/App.js

    I have created a sample of how I would approach something along these lines:

    Please Note: the order of the array matters as I am going off of the index to decide what song plays and what doesn't. You don't necessarily have to take this approach. This is just a quick and dirty solution.

    import { useEffect, useState } from "react";
    
    export default function App() {
      const [musicArray] = useState([
        "https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba-online-audio-converter.com_-1.wav",
        "https://www2.cs.uic.edu/~i101/SoundFiles/BabyElephantWalk60.wav"
      ]);
      const [data, setData] = useState([]);
    
      useEffect(() => {
        const musicData = musicArray.map((sound) => {
          return { audio: new Audio(sound), play: false };
        });
    
        setData(musicData);
      }, [musicArray]);
    
      const playSound = (index) => {
        setData((arr) =>
          arr.map((sound, i) => {
            if (i === index) {
              sound.audio.play();
              return { ...sound, play: true };
            }
            sound.audio.pause();
            return { ...sound, play: false };
          })
        );
      };
    
      useEffect(() => {
        console.log(data);
      }, [data]);
    
      const stopSound = (index) => {
        setData((arr) =>
          arr.map((sound, i) => {
            if (i === index) {
              sound.audio.pause();
              return { ...sound, play: false };
            }
            return { ...sound, play: false };
          })
        );
      };
    
      return (
        <div className="App">
          {data.map((sound, i) => {
            return (
              <>
                {sound.play ? (
                  <button onClick={() => stopSound(i)}>pause</button>
                ) : (
                  <button onClick={() => playSound(i)}>play</button>
                )}
              </>
            );
          })}
    
          <h2>Start editing to see some magic happen!</h2>
        </div>
      );
    }