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

React.js自定义音乐播放器开发求助:无法设置歌曲数组

React音乐播放器逻辑问题求助

我正在搭建一个带有上一曲、下一曲、播放和暂停按钮的自定义音乐播放器,已完成HTML按钮制作并添加了CSS样式。曾在原生JS(非React)中实现过相同设计且能正常运行,但在React.js的JS逻辑部分遇到困难。

我的需求是将歌曲设置为一个数组,通过该数组选择当前播放歌曲并注入到audio播放器的source中,但无法正确使用setSongs等状态方法。

以下是我的代码:

import React from 'react'
import back from "./rss/icons/back.png";
import next from "./rss/icons/next.png";
import pause from "./rss/icons/pause.png";
import play from "./rss/icons/play.png";
import song1 from "./rss/music/AgarTumSathHo.mp3";
import song2 from "./rss/music/AllOfMe.mp3";
import song3 from "./rss/music/CheapThrills.mp3";

export default function MusicPlayer() {
    const player = document.getElementById("player");
    const songList = [song1, song2, song3];
    let [songs, setSongs] = React.useState([song1]);
    let [isPlaying, setIsPlaying] = React.useState(false);
    let [nowPlaying, setNowPlaying] = React.useState(songs[0]);
    

    function TogglePlay() {
        if (!isPlaying) {
            setIsPlaying(true);
            player.play();
            document.getElementById('playIcon').style.display = "none";
            document.getElementById('pauseIcon').style.display = "inline";
        } else {
            setIsPlaying(false);
            player.pause();
            document.getElementById('playIcon').style.display = "inline";
            document.getElementById('pauseIcon').style.display = "none";
        }
    }

    function nextSong() {
        setIsPlaying(true);
        setNowPlaying(songs[nowPlaying + 1]);
        player.play();
    }

    return (
        <div id="media">
            <audio controls id="player">
                <source src={nowPlaying} type="audio/mp3" />
            </audio>
            <div class="inline">
                <div id="backLink" class="inline">
                    <button>
                        <li class="nav-button music-button" id="back">
                            <img id="backIcon" src={back} alt="back"/>
                        </li>
                    </button>
                </div>
                <div id="midLink" class="inline">
                    <button onClick={TogglePlay}>
                        <li class="nav-button music-button" id="playPause">
                            <img id="playIcon" src={play} alt="play" />
                            <img id="pauseIcon" src={pause} alt="pause" />
                        </li>
                    </button>
                </div>
                <div id="nextLink" class="inline">
                    <button onClick={nextSong}>
                        <li class="nav-button music-button" id="next">
                            <img id="nextIcon" src={next} alt="next"/>
                        </li>
                    </button>
                </div>
            </div>

            <div class="slidecontainer">
                <input type="range" min="1" max="100" value="50" class="slider" id="volSlider" />
            </div>

            <div id="nowPlaying">
                <p id="songName">Press Play</p>
            </div>
        </div>
  )
}

解决方案

你的代码主要问题在于直接操作DOM、状态管理逻辑错误,以及没有利用React的状态驱动视图特性。下面是修正后的代码,同时实现歌曲数组管理、播放/暂停、上下曲切换功能:

import React, { useRef, useState, useEffect } from 'react'
import back from "./rss/icons/back.png";
import next from "./rss/icons/next.png";
import pause from "./rss/icons/pause.png";
import play from "./rss/icons/play.png";
import song1 from "./rss/music/AgarTumSathHo.mp3";
import song2 from "./rss/music/AllOfMe.mp3";
import song3 from "./rss/music/CheapThrills.mp3";

// 定义歌曲数组,包含路径和名称
const songList = [
  { src: song1, name: "Agar Tum Sath Ho" },
  { src: song2, name: "All Of Me" },
  { src: song3, name: "Cheap Thrills" }
];

export default function MusicPlayer() {
  // 使用useRef获取audio元素,避免直接DOM查询
  const audioRef = useRef(null);
  // 当前播放歌曲的索引
  const [currentSongIndex, setCurrentSongIndex] = useState(0);
  // 播放状态
  const [isPlaying, setIsPlaying] = useState(false);

  // 当当前歌曲索引变化时,自动加载新歌曲并根据状态决定是否播放
  useEffect(() => {
    if (audioRef.current) {
      audioRef.current.load();
      if (isPlaying) {
        audioRef.current.play();
      }
    }
  }, [currentSongIndex]);

  // 播放/暂停切换
  const togglePlay = () => {
    if (audioRef.current) {
      if (isPlaying) {
        audioRef.current.pause();
      } else {
        audioRef.current.play();
      }
      setIsPlaying(!isPlaying);
    }
  };

  // 下一曲
  const nextSong = () => {
    setCurrentSongIndex(prev => (prev + 1) % songList.length);
  };

  // 上一曲
  const prevSong = () => {
    setCurrentSongIndex(prev => (prev - 1 + songList.length) % songList.length);
  };

  // 当前歌曲信息
  const currentSong = songList[currentSongIndex];

  return (
    <div id="media">
      {/* 使用useRef绑定audio元素 */}
      <audio ref={audioRef} controls>
        <source src={currentSong.src} type="audio/mp3" />
      </audio>
      <div className="inline">
        <div id="backLink" className="inline">
          <button onClick={prevSong}>
            <div className="nav-button music-button" id="back">
              <img id="backIcon" src={back} alt="back"/>
            </div>
          </button>
        </div>
        <div id="midLink" className="inline">
          <button onClick={togglePlay}>
            <div className="nav-button music-button" id="playPause">
              {/* 用状态控制图标显示,而非直接操作DOM */}
              <img 
                id="playIcon" 
                src={play} 
                alt="play" 
                style={{ display: isPlaying ? 'none' : 'inline' }} 
              />
              <img 
                id="pauseIcon" 
                src={pause} 
                alt="pause" 
                style={{ display: isPlaying ? 'inline' : 'none' }} 
              />
            </div>
          </button>
        </div>
        <div id="nextLink" className="inline">
          <button onClick={nextSong}>
            <div className="nav-button music-button" id="next">
              <img id="nextIcon" src={next} alt="next"/>
            </div>
          </button>
        </div>
      </div>

      <div className="slidecontainer">
        <input 
          type="range" 
          min="0" 
          max="100" 
          value="50" 
          className="slider" 
          id="volSlider"
          onChange={(e) => {
            if (audioRef.current) {
              audioRef.current.volume = e.target.value / 100;
            }
          }}
        />
      </div>

      <div id="nowPlaying">
        <p id="songName">{isPlaying ? currentSong.name : "Press Play"}</p>
      </div>
    </div>
  )
}

关键修正点:

  • 使用useRef替代DOM查询:React中应避免直接用document.getElementById,通过useRef绑定audio元素,更符合React的组件模型。
  • 状态驱动视图:用isPlaying状态控制播放/暂停图标的显示,而非直接修改DOM样式。
  • 正确管理歌曲索引:通过currentSongIndex状态跟踪当前播放歌曲在数组中的位置,上下曲切换时更新索引,利用取模实现循环播放。
  • useEffect处理歌曲切换逻辑:当currentSongIndex变化时,自动加载新歌曲并根据播放状态决定是否播放。
  • 修复class属性:React中元素的类名应使用className而非class。
  • 添加音量控制逻辑:给滑块添加onChange事件,直接控制audio元素的音量。
  • 优化歌曲数据结构:把歌曲路径和名称整合到数组对象中,方便显示当前歌曲名称。

内容的提问来源于stack exchange,提问作者Jani Dinner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:41:58