React使用useRef播放音频报DOMException无支持源错误解决方案求助
问题解答
方案可行性
使用React ref操作原生audio元素实现音频播放是完全可行的,这是React操作原生媒体元素的标准实现方案之一,不需要更换核心思路,仅需要修复现有逻辑的bug即可。
报错核心原因及修复点
- 初始src为空触发报错:初始state的
songs是空数组,组件首次渲染时audio标签的src会被赋值为空字符串,浏览器加载空源直接触发DOMException: Failed to load because no supported source was found报错。同时不要同时在audio标签绑定src、又在JS逻辑中动态修改src,两处同时操作容易出现状态冲突。 - 调用原生方法前未判断ref有效性:调用play、load等方法前,没有判断
songPlayer.current是否存在,页面刚挂载的极短时间内ref可能还没绑定到DOM元素,调用方法也会触发异常。 - 子组件调用不存在的方法:
MusicPlayer的prevSong逻辑里调用了props.playStop(),但父组件传递给子组件的属性里没有playStop方法,仅传递了playSong和pauseSong,执行上一首操作时会触发报错打断播放流程。 - 歌曲切换时序问题:当前
pickSong仅修改src,没有做基础的有效性校验,资源地址非法时直接加载也会触发报错。
核心逻辑调整示例
NavBar组件修改
import { useEffect, useRef, useState } from "react"; import MusicPlayer from "../Molecules/MusicPlayer"; import { songList } from "../../tools/songList"; function NavBar(props) { // 静态songList直接初始化,不用useEffect异步赋值避免初始为空 const [songs, setSongs] = useState(songList); const [isPlaying, setIsPlaying] = useState(false); const [songOn, setSongOn] = useState(0); const songPlayer = useRef(null); const pickSong = (index) => { // 增加有效性校验,避免非法赋值 if (!songPlayer.current || !songs[index]?.audio) return songPlayer.current.src = songs[index].audio; }; const playSong = () => { if (!songPlayer.current) return songPlayer.current.load(); songPlayer.current .play() .catch((err) => { console.info(err); }); }; const pauseSong = () => { if (!songPlayer.current) return songPlayer.current.pause(); }; return ( <> <div className="nav-bar"> <MusicPlayer songs={songs} playSong={playSong} currentSong={songPlayer} pauseSong={pauseSong} isPlaying={isPlaying} setIsPlaying={setIsPlaying} pickSong={pickSong} setSongOn={setSongOn} songOn={songOn} /> </div> {/* 去掉原生src绑定,完全通过ref控制资源地址 */} <audio ref={songPlayer}></audio> </> ); } export default NavBar;
MusicPlayer组件修改
// 仅修改prevSong内的错误方法调用即可 const prevSong = () => { if (props.songs[props.songOn - 1] !== undefined) { props.pickSong(props.songOn - 1); // 把不存在的playStop替换为传入的playSong props.playSong(); props.setSongOn(props.songOn - 1); props.setIsPlaying(true); } else { props.pickSong(props.songs.length - 1); props.playSong(); props.setSongOn(props.songs.length - 1); props.setIsPlaying(true); } };
额外排查点
- 检查
songList内的audio字段对应的资源地址是否正确,格式是否为浏览器支持的类型(mp3、wav、ogg等),如果是跨域资源要确认资源服务器配置了正确的CORS头。 - 本地开发使用相对路径时,确认音频资源放在项目public目录下,路径配置正确。
内容的提问来源于stack exchange,提问作者FRostri
相关产品推荐
相关产品推荐

