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

如何优化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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 14:43:13