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

React音乐播放器组件接收歌曲对象数组无法播放求助

问题:传入歌曲对象数组后音乐播放器无法播放

我开发了一个MusicPlayer组件,之前直接传入mp3文件数组时可以正常播放,但改成传入包含歌曲信息的对象数组后功能失效,怀疑是props接收和解构的问题。

相关代码

Music组件(父组件)

import React from 'react'

//Components
import MusicPlayer from '../components/MusicPlayer';

//Song Images 
import Drake from '../images/Drake.jpg'
import Adelle from '../images/adelle.png'
import Elvis from '../images/Elvis_hounddog.jpg'

//Song files
import DrakeOneDance from '../music/Drake_One_Dance.mp3'
import AdelleHello from '../music/Adele_Hello.mp3'
import ElvisHoundDog from '../music/Elvis.mp3'

//Song array 
const songs = [
  {
    artist: 'Drake',
    songName: 'One Dance',
    poster: Drake,
    songFile: DrakeOneDance
  },
  {
    artist: 'Adelle',
    songName: 'Hello',
    poster: Adelle,
    songFile: AdelleHello
  },
  {
    artist: 'Elvis',
    songName: 'Hound Dog',
    poster: Elvis,
    songFile: ElvisHoundDog
  },
]

function Music() {
    return (
      <div className='music-container'>
          <MusicPlayer songs={songs}/>
      </div>
    );
}

export default Music

MusicPlayer组件(子组件)

import React, { useState, useEffect, useRef } from 'react'

// MUI ICONS
import { Slider } from '@mui/material';
import SkipPreviousIcon from '@mui/icons-material/SkipPrevious';
import SkipNextIcon from '@mui/icons-material/SkipNext';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import PauseIcon from '@mui/icons-material/Pause';
import VolumeDownIcon from '@mui/icons-material/VolumeDown';
import VolumeUpIcon from '@mui/icons-material/VolumeUp';
import VolumeOffIcon from '@mui/icons-material/VolumeOff';
import VolumeMuteIcon from '@mui/icons-material/VolumeMute';
import IconButton from '@mui/material/IconButton'

function MusicPlayer({songs}) {
    //Song Files
    const songFiles = songs.songFile;
    
    //Hooks
    const audioPlayer = useRef()

    //State
    const [index, setIndex] = useState(0);
    const [currentSong] = useState(songFiles[index]);
    const [isPlaying, setisPlaying] = useState(false);
    const [volume, setVolume] = useState(30);
    const [mute, setMute] = useState(false);

    useEffect(() => {
        if(audioPlayer) {
            audioPlayer.current.volume = volume / 100;
        }
    }, [volume]);

    function togglePlay() {
        if(!isPlaying) {
            audioPlayer.current.play()
        } else {
            audioPlayer.current.pause()
        }
        setisPlaying(isPlaying => !isPlaying)
    }

    function toggleSkipForward() {
        if(index >= songFiles.length - 1) {
            setIndex(0);
            audioPlayer.current.src = songFiles[0];
            audioPlayer.current.play();
        } else {
            setIndex(prev => prev + 1);
            audioPlayer.current.src = songFiles[index + 1];
            audioPlayer.current.play();
        }
    }

    function toggleSkipBackward() {
        if(index > 0) {
            setIndex(prev => prev -1);
            audioPlayer.current.src = songFiles[index -1];
            audioPlayer.current.play();
        } 
    }
    
    function VolumeBtns() {
        return mute
            ? <VolumeOffIcon sx={{color: 'lime', '&:hover': {color: 'white'}}} onClick={() => setMute(!mute)} />
            : volume <= 20 ? <VolumeMuteIcon sx={{color: 'lime', '&:hover': {color: 'white'}}} onClick={() => setMute(!mute)} />
            : volume <= 75 ? <VolumeDownIcon sx={{color: 'lime', '&:hover': {color: 'white'}}} onClick={() => setMute(!mute)} />
            : <VolumeUpIcon sx={{color: 'lime', '&:hover': {color: 'white'}}} onClick={() => setMute(!mute)} />
    }

  return (
    <div className='music-player-container'>
        <audio src={currentSong} ref={audioPlayer} muted={mute} />    
        <div className="controls">
            <div className="volume">
                <IconButton>
                    <VolumeBtns />
                </IconButton>
                <Slider min={0} max={100} value={volume} onChange={(e,v) => setVolume(v)}
                defaultValue={50}
                    sx={{
                    width: 100,
                    color: 'success.main',
                    margin: '20px',
                    }} />
            </div>
            <div className="control-panel">
                <IconButton onClick={toggleSkipBackward}>
                    <SkipPreviousIcon
                    sx={{
                        margin: '10px',
                        cursor: 'pointer',
                        color: 'silver', '&:hover': {color: 'blue'}
                    }} />
                </IconButton>
                {!isPlaying
                  ?  <IconButton onClick={togglePlay}>
                        <PlayArrowIcon sx={{ margin: '10px', cursor: 'pointer', color: 'silver', '&:hover': {color: 'blue'} }} />
                    </IconButton>
                   : <IconButton onClick={togglePlay}>
                        <PauseIcon sx={{ margin: '10px', cursor: 'pointer', color: 'silver', '&:hover': {color: 'blue'} }} />
                    </IconButton>
                }
                <IconButton onClick={toggleSkipForward}>
                    <SkipNextIcon
                    sx={{
                        margin: '10px',
                        cursor: 'pointer',
                        color: 'silver', '&:hover': {color: 'blue'}
                    }} />
                </IconButton>
            </div>
        </div>
    </div>
  )
}

export default MusicPlayer

错误分析与修复方案

1. 错误提取歌曲文件数组

songs是对象数组,原代码中songs.songFile会返回undefined(数组本身没有songFile属性),需要从每个对象中提取:

// 错误写法
const songFiles = songs.songFile;

// 正确写法
const songFiles = songs.map(song => song.songFile);

2. currentSong状态未同步index变化

原代码中currentSong仅在初始化时赋值,index变化时不会自动更新,建议直接通过index计算,无需单独状态:

// 移除原currentSong状态定义
// const [currentSong] = useState(songFiles[index]);

// 在audio标签中直接使用
<audio src={songFiles[index]} ref={audioPlayer} muted={mute} />    

3. 跳转函数边界逻辑优化

原toggleSkipBackward在index为0时无处理,添加循环到最后一首的逻辑,同时简化跳转计算:

function toggleSkipForward() {
    const nextIndex = (index + 1) % songFiles.length;
    setIndex(nextIndex);
    audioPlayer.current.src = songFiles[nextIndex];
    audioPlayer.current.play();
}

function toggleSkipBackward() {
    const prevIndex = (index - 1 + songFiles.length) % songFiles.length;
    setIndex(prevIndex);
    audioPlayer.current.src = songFiles[prevIndex];
    audioPlayer.current.play();
}

4. useEffect依赖修正

原useEffect中判断audioPlayer存在,但应直接判断audioPlayer.current:

useEffect(() => {
    if(audioPlayer.current) {
        audioPlayer.current.volume = volume / 100;
    }
}, [volume]);

内容的提问来源于stack exchange,提问作者Gavin Walsh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 18:45:48