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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 11:42:02