You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React音频播放器切换音频时旧音频未终止的问题求解

解决音频切换时旧音频不终止的问题

问题核心在于切换音频时,旧的Audio实例没有被主动暂停和清理,仍在后台播放。直接设置audioRef.current = null无法终止音频播放,必须主动调用暂停方法并重置相关状态。

修改方案

在AudioFunctionality组件中,监听currentItem变化时先处理旧音频实例,再创建新实例,同时重置播放状态:

  1. 当currentItem变更时,检查是否存在旧音频实例,若存在则暂停播放、重置进度并释放资源
  2. 重置isPlaying为false,确保切换音频后默认处于暂停状态
  3. 最后创建新的音频实例

修改后的完整代码

import over from "./over.wav";
import pretty from "./pretty1.mp3";
import diary from "./diary.mp3";
import { useState, useEffect, useRef } from "react";
import { TbPlayerPause, TbPlayerPlay } from "react-icons/tb";

const data = [
  {
    id: 1,
    musicSrc: over,
  },
  {
    id: 2,
    musicSrc: pretty,
  },
  {
    id: 3,
    musicSrc: diary,
  },
];

const App = () => {
  const [currentItem, setCurrentItem] = useState(2);
  return (
    <div className="audio-container">
      <BtnSet
        data={data}
        currentItem={currentItem}
        setCurrentItem={setCurrentItem}
      />
    </div>
  );
};

const BtnSet = ({ data, currentItem, setCurrentItem }) => {
  function handleCurrentItem(index) {
    setCurrentItem(index);
  }
  return (
    <>
      {data.map((audioItem, index) => {
        return (
          <button key={audioItem.id} onClick={() => handleCurrentItem(index)}>
            Audio {index + 1}
          </button>
        );
      })}
      <AudioFunctionality data={data} currentItem={currentItem} />
    </>
  );
};

const AudioFunctionality = ({ data, currentItem }) => {
  const [isPlaying, setIsPlaying] = useState(false);
  const audioRef = useRef(null);

  useEffect(() => {
    // 清理旧音频实例:暂停播放+重置进度+释放资源
    if (audioRef.current) {
      audioRef.current.pause();
      audioRef.current.currentTime = 0;
      audioRef.current.src = "";
      audioRef.current = null;
    }
    // 重置播放状态
    setIsPlaying(false);
    // 创建新音频实例
    audioRef.current = new Audio(data[currentItem].musicSrc);
  }, [currentItem, data]);

  useEffect(() => {
    if (!audioRef.current) return;
    if (isPlaying) {
      audioRef.current.play().catch((err) => {
        console.log("Failed to start playback ", err);
      });
    } else {
      audioRef.current.pause();
    }
  }, [isPlaying]);

  return (
    <AudioControls isPlaying={isPlaying} onPlayPauseClick={setIsPlaying} />
  );
};

const AudioControls = ({ isPlaying, onPlayPauseClick }) => {
  return (
    <div>
      {isPlaying ? (
        <button
          type="button"
          className="play-pause-btn"
          onClick={() => onPlayPauseClick(false)}
          aria-label="Pause"
        >
          <TbPlayerPause className="play-btn" />
        </button>
      ) : (
        <button
          type="button"
          className="play-pause-btn"
          onClick={() => onPlayPauseClick(true)}
          aria-label="Play"
        >
          <TbPlayerPlay />
        </button>
      )}
    </div>
  );
};

export default App;

关键改动说明

  • 在监听currentItem的useEffect中,先对旧音频实例执行暂停、重置进度和清空资源的操作,彻底终止旧音频播放
  • 调用setIsPlaying(false),保证切换音频后播放按钮默认显示为“播放”状态
  • 修复了AudioControls中用<p>标签充当按钮的语义化问题,改为标准<button>标签

内容的提问来源于stack exchange,提问作者Don199

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 05:35:13