reactjsaudiohtml5-audio

How to pause the previous audio when I play a new audio in React?


When I click on the 'PlayCircleFilled' icon the audio plays perfectly, the problem is that if the user clicks on different 'PlayCircleFilled' icons all the audios are played at the same time.

What I want to achieve is to stop the audio being played when the user clicks on a new 'PlayCircleFilled' icon so that only one audio is played at a time, the last clicked audio is played.

My code is as follows:

const Client = (props) => {

  const {
    sentence,
    typeConversation
  } = props;

  const [audioUrl, setAudioUrl] = useState(null);
  const audioRef = useRef(null);

  const handleAudioPlay = (url) => {
    if (audioRef.current) {
      audioRef.current.pause();
      audioRef.current.currentTime = 0;
    }
    setAudioUrl(url);
  };

  const handleAudioEnded = () => {
    setAudioUrl(null); 
  };

  const renderTextWithAudioPlayer = (text) => {
    const combinedRegex = /(https?:\/\/[^\s]+)|(\[Audio\])/g;
    const parts = text.split(combinedRegex).filter(part => part !== undefined && part !== "");

    let renderedElements = [];

    parts.forEach((part, index) => {
      if (/https?:\/\/[^\s]+/.test(part)) {
        const fileExtension = part.split('.').pop().toLowerCase();
        if (fileExtension === 'wav' || fileExtension === 'mp3' || fileExtension === 'ogg') {
          renderedElements.push(
            <Tooltip key={index} placement="bottom" title="Reproducir audio">
              <PlayCircleFilled className="play-audio-icon" onClick={() => handleAudioPlay(part)} />
            </Tooltip>
          );
        } else {
          renderedElements.push(
            <a key={index} href={part} target="_blank" rel="noopener noreferrer">
              {part}
            </a>
          );
        }
      } else if (/\[Audio\]/.test(part)) {
        renderedElements.push(<AudioFilled className="play-audio-icon" key={index} />);
      } else {
        renderedElements.push(<span key={index} dangerouslySetInnerHTML={{ __html: part }} />);
      }
    });
    return renderedElements;
  };

  return(
    <div className="chat-message" id={sentence.id}>
      <div className="chat-message__text">
      {
        typeConversation == 'audio' ?
        <h6 className="chat-message__title chat-message__title--client">
          Cliente | {sentence.start_time}
        </h6>
        :
        <h6 className="chat-message__title chat-message__title--client">
          Cliente | {sentence.date} {sentence.time}
        </h6>
      }
        <p>
          {renderTextWithAudioPlayer(sentence.text)}
        </p>
        {audioUrl && (
          <audio controls autoPlay ref={audioRef} onEnded={handleAudioEnded} style={{ display: 'none' }}>
            <source src={audioUrl} type="audio/mpeg" />
            Su navegador no admite la reproducción de audio.
          </audio>
        )}
      </div>
    </div>
  );
}

export default Client;

Thank you very much for your time and I hope you can help me!


Solution

  • Maintain a list of refs for every rendered <audio> and pause the others when one plays.

    See complete example

    function AudioPlayers({ urls }) {
      const refs = []   
      const newRef = () => refs[refs.push(createRef()) - 1]
      const onPlay = event => {
        refs.forEach(ref => (ref.current !== event.target) && ref.current.pause())
      }
    
      return urls.map(src => <audio controls {...{ref: newRef(), src, onPlay}} />)
    }