DEV Community

Cover image for How to play audio with React
Uralys
Uralys

Posted on

4 1 1

How to play audio with React

How to play audio with React

In this article, I'll show you how I play audio in my React frontends.

Previously in this article I set up a list of Lottie animations displaying a music track being played when the user hovers the area.

I now need to play an audio at the same time.

setting up the audio

I have 4 audio files somewhere on S3, distributed by CloudFront:

const musics = [
  'https://alterego.community/audio/classique.mp4',
  'https://alterego.community/audio/folk.mp4',
  'https://alterego.community/audio/electro.mp4',
  'https://alterego.community/audio/hip-hop.mp4'
];
Enter fullscreen mode Exit fullscreen mode

For each music, I create an audio element and load it on mount:

const [audio, setAudio] = useState<HTMLAudioElement>();

useEffect(() => {
  const _audio = new Audio(musics[props.num - 1]);
  _audio.load();
  _audio.addEventListener('canplaythrough', () => {
    setAudio(_audio);
  });
}, []);
Enter fullscreen mode Exit fullscreen mode

Listening for the user's interaction

I add a React ref on a parent element of the Lottie animation to listen for hover and touch events:

const musicRef = useRef<HTMLDivElement>(null);

<$Music ref={musicRef}>
  <Lottie hover loop={true} src={musicAnimation} />
</$Music>
Enter fullscreen mode Exit fullscreen mode

Now when audio is ready, I can listen for the user's interaction:

useEffect(() => {
  if (!audio) return;
  if (!musicRef) return;

  const play = async () => {
    audio.play();
  };

  const stop = () => {
    audio.pause();
    audio.currentTime = 0;
  };

  const element = musicRef.current;

  if (element) {
    element.addEventListener('mouseenter', play);
    element.addEventListener('mouseleave', stop);
    element.addEventListener('touchstart', play);
    element.addEventListener('touchend', stop);

    return () => {
      element.removeEventListener('mouseenter', play);
      element.removeEventListener('mouseleave', stop);
      element.removeEventListener('touchstart', play);
      element.removeEventListener('touchend', stop);
    };
  }
}, [musicRef, audio]);
Enter fullscreen mode Exit fullscreen mode

Plugging the state

We need to store the selected music in the state.

I handle the state the same way I did in the article about La Taverne:

The action + reducing in my barrel:

export const PICK_MUSIC = '@@user/PICK_MUSIC';

type PickMusicPayload = {musicChoice: MusicChoice};

export type PickMusicAction = {
  type: '@@user/PICK_MUSIC';
  payload: PickMusicPayload;
};

const onPickMusic = {
  on: PICK_MUSIC,
  reduce: (state: State, payload: PickMusicPayload) => {
    const {musicChoice} = payload;
    state.preferredMusic = musicChoice;
  }
};
Enter fullscreen mode Exit fullscreen mode

Then the React part:

import {useTaverne} from 'taverne/hooks';

// ... in the component:

const {dispatch, pour} = useTaverne();
const preferredMusic = (pour('user.preferredMusic') || -1) as MusicChoice;

const pickMusic = (musicChoice: MusicChoice) => () => {
  dispatch({
    type: PICK_MUSIC,
    payload: {musicChoice}
  } as PickMusicAction);
};

<Music
  num={musicNum}
  selected={preferredMusic === musicNum}
  onClick={pickMusic(musicNum)}
/>
Enter fullscreen mode Exit fullscreen mode

Mounting it all together

then in the parent component rendering all musics:

type MusicChoice = -1 | 1 | 2 | 3 | 4;
const musicChoices: Array<MusicChoice> = [1, 2, 3, 4];

<$Musics>
  {musicChoices.map(musicNum => (
    <Music
      key={`music-${musicNum}`}
      num={musicNum}
      selected={preferredMusic === musicNum}
      onClick={pickMusic(musicNum)}
    />
  ))}
</$Musics>
Enter fullscreen mode Exit fullscreen mode

Thanks for reading, see you around!

As always, feel free to comment on parts you need more explanations for, or share your thoughts on how you would have handled the parts you disagree with.

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

πŸ‘‹ Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay