React遍历渲染歌曲列表点击播放时永远播放第一首歌如何解决
问题根因
- HTML 规范要求
id属性必须全局唯一,你在循环渲染所有audio元素时都使用了相同的id="audioFile",document.getElementById只会返回页面中第一个匹配该id的元素,也就是第一首歌对应的audio,所以无论点击哪个播放按钮,操作的都是第一首的音频。 - 你在组件顶层直接执行
let x = document.getElementById('audioFile')的时机错误,组件首次渲染时DOM还未挂载到页面,此时x永远为null,逻辑本身存在隐患。 - React 项目中不推荐直接用
getElementById等原生DOM选择器,优先使用ref绑定DOM元素,避免匹配错误。
修复方案
方案1:全局单audio实例(推荐)
无需循环渲染多个audio标签,全局只保留一个audio实例,点击对应歌曲时动态替换音源地址即可,大幅减少DOM冗余:
import React, { useState, useEffect, useRef } from 'react' import axios from 'axios' const Home = () => { const [songs, setSongs] = useState([]) // 全局audio引用 const audioRef = useRef(null) useEffect(() => { setSongs([ { id: 1, title: 'Song 1', genre: 'Instrumental' }, { id: 2, title: 'Song 2', genre: 'Western' } ]) }, []) const playSong = (songId) => { if(!audioRef.current) return // 动态替换音源 audioRef.current.src = `http://localhost:8000/api/songs/audio/${songId}` // 重新加载后播放 audioRef.current.load() audioRef.current.play() } return ( <> {/* 全局唯一audio标签 */} <audio ref={audioRef} /> { songs.map(song => ( <div className='music-controller' key={song.id}> <h2>{song.title}</h2> <p><small>{song.genre}</small></p> <div className="controls"> {/* 点击时传入当前歌曲id */} <button onClick={() => playSong(song.id)}>Play</button> </div> </div> )) } </> ) } export default Home
方案2:保留多audio实例的修复方法
如果你确实需要每个歌曲对应独立的audio实例,就不要使用重复id,通过ref数组存储每个audio的引用即可:
import React, { useState, useEffect, useRef } from 'react' import axios from 'axios' const Home = () => { const [songs, setSongs] = useState([]) // 用数组存储所有audio的ref const audioRefs = useRef([]) useEffect(() => { setSongs([ { id: 1, title: 'Song 1', genre: 'Instrumental' }, { id: 2, title: 'Song 2', genre: 'Western' } ]) }, []) const playSong = (index) => { const targetAudio = audioRefs.current[index] if(targetAudio){ targetAudio.play() } } return ( <> { songs.map((song, index) => ( <div className='music-controller' key={song.id}> <h2>{song.title}</h2> <p><small>{song.genre}</small></p> <div className="controls"> <button onClick={() => playSong(index)}>Play</button> <audio ref={el => audioRefs.current[index] = el}> <source src={`http://localhost:8000/api/songs/audio/${song.id}`} /> </audio> </div> </div> )) } </> ) } export default Home
内容的提问来源于stack exchange,提问作者Lun
相关产品推荐
相关产品推荐

