如何优化React流媒体播放器?移除document.getElementById
React流媒体播放器移除DOM操作解决方案
现有React流媒体播放器代码可正常运行,但希望移除代码中的document.getElementById DOM操作,已尝试通过Audio实例的paused属性控制按钮样式,但不清楚如何处理暂停逻辑以及audio.play()返回值的判断逻辑,以下是完整改造方案:
改造后完整代码
import { useState, useRef, useEffect } from "react"; import { FaPlayCircle, FaPauseCircle } from "react-icons/fa"; import { GetConfiguration } from "../../../api"; import { Base, Column, Flex, Button } from "../../../common"; export const RadioView = () => { // 用useRef存储Audio实例,避免组件重渲染重复创建,且无需触发状态更新 const audioRef = useRef(new Audio(GetConfiguration<string>("radio.stream.url"))); const [isPlaying, setIsPlaying] = useState(false); // 组件挂载时初始化音频属性,添加事件监听 useEffect(() => { const audio = audioRef.current; audio.autoplay = true; audio.volume = 0.3; // 监听音频播放/暂停事件,同步状态(比如音频自动暂停时更新状态) const handlePlay = () => setIsPlaying(true); const handlePause = () => setIsPlaying(false); audio.addEventListener('play', handlePlay); audio.addEventListener('pause', handlePause); // 组件卸载时清理事件监听和音频实例,避免内存泄漏 return () => { audio.removeEventListener('play', handlePlay); audio.removeEventListener('pause', handlePause); audio.pause(); audio.src = ''; }; }, []); const startRadio = async () => { try { const resp = audioRef.current.play(); // 兼容部分不返回Promise的浏览器 if (resp) await resp; setIsPlaying(true); } catch (err) { // 捕获播放失败异常(比如浏览器自动播放限制) console.error('播放失败:', err); } }; const pauseRadio = () => { audioRef.current.pause(); setIsPlaying(false); }; const updateVolume = (volume) => { let radioVolume = Math.round(volume); radioVolume = Math.max(0, Math.min(100, radioVolume)); audioRef.current.volume = radioVolume / 100; }; return ( <Base className="notification-radio rounded"> <Column> <Flex gap={1}> <Button type="button" style={{ backgroundColor: "#F1B941", color: isPlaying ? "blue" : "black", cursor: "pointer" }} onClick={startRadio} > <FaPlayCircle className="radio-font-icons fa-icon" /> </Button> <Button type="button" style={{ backgroundColor: "#F1B941", color: isPlaying ? "red" : "black", cursor: "pointer" }} onClick={pauseRadio} > <FaPauseCircle className="radio-font-icons fa-icon" /> </Button> </Flex> <input onChange={(e) => updateVolume(e.target.value)} style={{ marginTop: "7px", display: "inline-block" }} type="range" className="form-range" min="0" max="100" step="1" /> <div className="col-md-1" /> </Column> </Base> ); };
关键改造说明
- 替换Audio实例存储方式:用
useRef替代useState存储Audio实例,因为音频实例是可变对象,不需要触发组件重渲染,同时避免组件每次渲染重复创建实例。 - 用状态控制样式:新增
isPlaying状态,完全替代DOM操作来控制按钮颜色,符合React单向数据流原则。 - 处理
audio.play()返回值:使用async/await处理返回的Promise,兼容不返回Promise的浏览器,同时捕获播放失败的异常(比如浏览器自动播放限制)。 - 添加事件监听同步状态:监听音频的
play和pause事件,确保状态和实际播放状态一致(比如音频因其他原因自动暂停时,状态也会同步更新)。 - 组件清理:在组件卸载时移除事件监听并清理音频实例,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Duckietm
相关产品推荐
相关产品推荐

