如何结合播放列表使用ReactHowler?加载时报错求助
问题分析与解决方案
Hey,咱们一步步拆解你的问题并修复它:你遇到的TypeError: Cannot read property 'length' of undefined,核心是ReactHowler组件挂载时就尝试加载初始值为null的src——Howler要求src是字符串或者音频URL数组,传null的话,内部代码读取length属性就直接报错了。另外你的代码还有状态管理和数据遍历的小问题,下面逐个解决:
1. 先搞定ReactHowler的初始加载问题
首先,咱们给ReactHowler加个条件渲染,只有当有明确的音频URL时才渲染它,避免初始加载空值:
{playerState.currentTrack && ( <ReactHowler src={playerState.currentTrack} playing={playerState.isPlaying} // 音频播放结束后自动暂停 onEnd={() => setPlayerState(prev => ({...prev, isPlaying: false}))} /> )}
同时,把状态的结构改得更清晰,避免混淆:
const [playerState, setPlayerState] = useState({ currentTrack: null, // 当前播放的音频URL isPlaying: false // 播放状态 });
2. 修正数据遍历与状态更新逻辑
你的代码里有几个容易踩坑的点:
data[title]里的title没定义哦!得根据你实际获取的JSON结构来,比如如果返回的是{ "myPlaylist": { "tracks": [...] } },那就要写成data.myPlaylist.tracks- 如果
tracks是数组的话,直接用tracks.map就行,不用绕Object.keys - 点击按钮时的状态更新逻辑错了:之前的
!play是取整个状态对象的布尔值,而不是取play属性,这会导致状态切换异常
修正后的Async.Fulfilled部分代码:
<Async.Fulfilled> {(data) => { // 这里替换成你JSON实际的tracks路径,比如data.tracks或者data.playlist.tracks const tracks = data?.playlist?.tracks || []; if (tracks.length === 0) return <li>暂无曲目</li>; return tracks.map((track) => ( <li key={track.filename}> <button onClick={() => { // 逻辑:如果点击的是当前正在播放的曲目,就切换播放状态;否则切换曲目并自动播放 setPlayerState(prev => { const isSameTrack = prev.currentTrack === track.filename; return { currentTrack: track.filename, isPlaying: isSameTrack ? !prev.isPlaying : true }; }); }} > {track.title} {/* 给当前播放的曲目加个标识,用户体验更好 */} {playerState.currentTrack === track.filename && playerState.isPlaying && ' ▶️'} </button> </li> )); }} </Async.Fulfilled>
3. 完整的修复后代码
把所有修改整合起来,就是这样:
import React, { useState } from 'react'; import ReactHowler from 'react-howler'; import Async from 'react-async'; const loadTracks = () => fetch('你的播放列表URL') .then(res => res.ok ? res.json() : Promise.reject(res)) .catch(err => console.error('加载曲目失败:', err)); const Music = () => { const [playerState, setPlayerState] = useState({ currentTrack: null, isPlaying: false }); return ( <ul className="music"> {/* 只有存在当前曲目时才渲染播放器 */} {playerState.currentTrack && ( <ReactHowler src={playerState.currentTrack} playing={playerState.isPlaying} onEnd={() => setPlayerState(prev => ({...prev, isPlaying: false}))} /> )} <Async promiseFn={loadTracks}> <Async.Loading>Loading...</Async.Loading> <Async.Fulfilled> {(data) => { // 替换成你JSON实际的tracks路径 const tracks = data?.tracks || []; if (tracks.length === 0) return <li>暂无曲目</li>; return tracks.map((track) => ( <li key={track.filename}> <button onClick={() => { setPlayerState(prev => { const isSameTrack = prev.currentTrack === track.filename; return { currentTrack: track.filename, isPlaying: isSameTrack ? !prev.isPlaying : true }; }); }} > {track.title} {playerState.currentTrack === track.filename && playerState.isPlaying && ' ▶️'} </button> </li> )); }} </Async.Fulfilled> <Async.Rejected> {(error) => `加载失败: ${error.message || '未知错误'}`} </Async.Rejected> </Async> </ul> ); }; export default Music;
4. 更简单的替代方案:用react-audio-player
如果你觉得Howler有点重,也可以试试react-audio-player——它是基于原生<audio>标签的封装,学习成本低,适合简单的播放场景:
先安装:
npm install react-audio-player
然后替换ReactHowler的部分:
import ReactAudioPlayer from 'react-audio-player'; // 在组件中替换成: {playerState.currentTrack && ( <ReactAudioPlayer src={playerState.currentTrack} autoPlay={playerState.isPlaying} controls // 可以显示原生播放控件,也可以自己自定义 onEnded={() => setPlayerState(prev => ({...prev, isPlaying: false}))} /> )}
内容的提问来源于stack exchange,提问作者Swimmer
相关产品推荐
相关产品推荐

