React子组件LessonCard控制AudioPlayer播放暂停失效问题
问题原因分析
- AudioPlayer未同步播放状态:仅在
audioUri变化时初始化播放器,后续isAudioPlaying状态变更无法触发WaveSurfer实例的播放/暂停操作。 - LessonCard点击逻辑冗余:每次点击都强制设置
audioUri,即使是同一音频也会触发AudioPlayer重新加载,导致暂停操作被覆盖。 - 状态双向绑定缺失:WaveSurfer自身的播放状态未与React的
isAudioPlaying同步,仅初始化时处理一次,后续状态变更脱节。
修复方案
1. 给AudioPlayer添加状态监听,同步控制播放器
修改AudioPlayer.jsx,新增监听isAudioPlaying的useEffect,同时绑定WaveSurfer的播放/暂停事件同步React状态:
import React, { useEffect, useRef } from "react"; import WaveSurfer from "wavesurfer.js"; const AudioPlayer = ({ audioUri, isAudioPlaying, setIsAudioPlaying }) => { const playerRef = useRef(null); const wavesurferRef = useRef(null); const handlePlay = () => { if (wavesurferRef.current) { wavesurferRef.current.togglePlay(); // 直接调用WaveSurfer的切换方法 } }; const downloadAudio = () => { const link = document.createElement("a"); link.href = audioUri; link.download = "your-audio.mp3"; document.body.appendChild(link); link.click(); document.body.removeChild(link); setTimeout(() => link.remove(), 50); }; useEffect(() => { wavesurferRef.current = WaveSurfer.create({ container: playerRef.current, waveColor: "#BFCEBF", progressColor: "#1e8a69", cursorColor: "transparent", barWidth: 3, barHeight: 2, barGap: 2, barRadius: 5, height: 60, normalize: true, responsive: true, dragToSeek: true, autoScroll: true, }); // 绑定WaveSurfer事件,同步到React状态 wavesurferRef.current.on("play", () => setIsAudioPlaying(true)); wavesurferRef.current.on("pause", () => setIsAudioPlaying(false)); if (audioUri) { wavesurferRef.current.load(audioUri); wavesurferRef.current.on("ready", () => { isAudioPlaying ? wavesurferRef.current.play() : wavesurferRef.current.pause(); }); } return () => wavesurferRef.current.destroy(); }, [audioUri]); // 新增:监听React状态变化,同步控制播放器 useEffect(() => { if (wavesurferRef.current) { isAudioPlaying ? wavesurferRef.current.play() : wavesurferRef.current.pause(); } }, [isAudioPlaying]); return ( <div className="container mx-auto 2xl:max-w-[1280px]"> <div className={`${ audioUri ? "fixed bottom-0 left-0 z-20 bg-slate-100 w-full px-4 py-2 flex items-center justify-center translate-y-[0%] [transition:300ms]" : "translate-y-[100%]" }`} > <div className="max-w-[37rem] mx-auto w-full flex items-center"> <div onClick={handlePlay} className={`${audioUri ? "flex items-center justify-center mr-4" : "hidden"}`} > <button className="w-[35px] h-[35px] sm:w-[40px] sm:h-[40px] rounded-full bg-[var(--deep-green)]"> <i className={`fa ${isAudioPlaying ? "fa-pause" : "fa-play"} text-white duration-150`} ></i> </button> </div> <div ref={playerRef} className="player w-full z-40 gap-2 sm:gap-4"> <div onClick={downloadAudio} className={`${audioUri ? "flex items-center justify-center" : "hidden"}`} > <button className="w-[35px] h-[35px] sm:w-[40px] sm:h-[40px] rounded-full bg-slate-200 hover:bg-[var(--deep-green)] group duration-150"> <i className="fa fa-download text-[var(--deep-green)] group-hover:text-white duration-150"></i> </button> </div> </div> </div> </div> </div> ); }; export default AudioPlayer;
2. 修改LessonCard点击逻辑,避免重复加载音频
首先在Home.jsx中给LessonCard传递当前激活的音频地址:
<LessonCard key={idx} lessonTitle={lessonTitle} category={category} publishDate={uploadDate} audioUri={audio} currentActiveAudio={audioUri} // 新增:传递当前播放的音频地址 isAudioPlaying={audioUri === lesson.audio && isAudioPlaying} setIsAudioPlaying={setIsAudioPlaying} setAudioUri={setAudioUri} />
然后修改LessonCard.jsx的点击逻辑,仅在切换音频时设置audioUri:
import React, { useRef } from "react"; const LessonCard = ({ lessonTitle, category, publishDate, audioUri, currentActiveAudio, isAudioPlaying, setIsAudioPlaying, setAudioUri, }) => { const lessonCardRef = useRef(null); const handleCardPlay = () => { if (currentActiveAudio !== audioUri) { // 切换到新音频,直接播放 setAudioUri(audioUri); setIsAudioPlaying(true); } else { // 同一音频,仅切换播放状态 setIsAudioPlaying((prev) => !prev); } }; return ( <div ref={lessonCardRef} onClick={handleCardPlay} className="lesson-card w-full bg-white border border-slate-200 hover:border-[color:var(--deep-green)] duration-150 h-[70px] px-2 rounded-[7px] cursor-pointer group flex justify-between active:border-[color:var(--deep-green)]" > <div className="w-full h-full flex items-center"> <div className="h-full flex items-center justify-center"> <button className="[color:var(--deep-green)] bg-slate-100 w-[40px] h-[40px] rounded-full group-hover:bg-slate-500 group-hover:text-white duration-150 flex items-center justify-center font-semibold text-lg mr-3"> <i className={isAudioPlaying ? "fa fa-pause" : "fa fa-play"}></i> </button> </div> <div className="flex-1"> <p className="text-lg truncate text-ellipsis line-clamp-1 break-words whitespace-normal"> {lessonTitle} </p> <div className="flex items-center justify-between"> <p className="text-sm capitalize text-[color:rgba(0,0,0,0.7)]"> {category} </p> <p className="[font-size:0.75rem] text-[color:rgba(0,0,0,0.7)]"> {publishDate} </p> </div> </div> <div className="hidden w-[40px] h-[40px] rounded-full bg-slate-200 items-center justify-center"> <button> <i className="fa fa-download"></i> </button> </div> </div> </div> ); }; export default LessonCard;
修复效果验证
- 点击未播放的卡片:音频加载并自动播放,按钮图标切换为暂停。
- 点击正在播放的卡片:音频暂停,按钮图标切换为播放。
- 点击其他卡片:当前音频暂停,新音频加载并播放。
- 点击底部播放器的控制按钮:卡片图标与播放状态同步变化。
内容的提问来源于stack exchange,提问作者shahid khan
相关产品推荐
相关产品推荐

