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

如何结合播放列表使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 18:37:41