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

如何用React+TypeScript实现鼓机?现有代码报错求助

问题分析与修复方案

你的代码存在几个关键问题导致声音无法播放,以下是逐一修复思路及最终代码:

核心问题点

  1. 参数传递错误:点击按钮和键盘触发时,你传入handlePlay的是声音名称(如"Kick"),但实际需要传入音频资源路径;同时显示区域应该展示声音名称而非资源路径。
  2. Howl配置错误:Howl构造函数的src参数要求为数组类型,你传入的单个字符串会导致音频加载失败。
  3. 重复创建实例浪费资源:每次播放都新建Howl实例,建议提前初始化所有声音实例,提升性能。
  4. TypeScript类型缺失:未定义sound对象的明确类型,使用any会降低代码健壮性。

修复后的完整代码

import { useState, useEffect } from "react";
import "./App.css";
import kick from "./assets/kick.mp3";
import snare from "./assets/snare.mp3";
import hiHat from "./assets/hi-hat.mp3";
import tom from "./assets/tom.mp3";
import clap from "./assets/clap.mp3";
import shaker from "./assets/shaker.mp3";
import tambourine from "./assets/tambourine.mp3";
import cymbal from "./assets/cymbal.mp3";
import cowbell from "./assets/cowbell.mp3";
import { Howl, Howler } from "howler";

// 定义Sound对象的TypeScript类型
type Sound = {
  key: string;
  name: string;
  src: string;
  soundInstance?: Howl;
};

const sounds: Sound[] = [
  { key: "Q", name: "Kick", src: kick },
  { key: "W", name: "Snare", src: snare },
  { key: "E", name: "Hi-hat", src: hiHat },
  { key: "A", name: "Tom", src: tom },
  { key: "S", name: "Clap", src: clap },
  { key: "D", name: "Shaker", src: shaker },
  { key: "Z", name: "Tambourine", src: tambourine },
  { key: "X", name: "Cymbal", src: cymbal },
  { key: "C", name: "Cowbell", src: cowbell },
];

function App() {
  const [display, setDisplay] = useState("");

  // 初始化所有Howl实例,仅在组件挂载时执行一次
  useEffect(() => {
    sounds.forEach(sound => {
      sound.soundInstance = new Howl({
        src: [sound.src] // src必须传入数组格式
      });
    });
    Howler.volume(1.0);
  }, []);

  const handlePlay = (sound: Sound) => {
    setDisplay(sound.name);
    sound.soundInstance?.play();
  };

  const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
    const sound = sounds.find((s) => s.key === event.key.toUpperCase());
    if (sound) handlePlay(sound);
  };

  return (
    <div id="drum-machine" onKeyDown={handleKeyDown} tabIndex={0}>
      <div id="display">{display}</div>
      <div id="pad-container">
        {sounds.map((sound) => (
          <button
            className="drum-pad"
            id={sound.name}
            key={sound.key}
            onClick={() => handlePlay(sound)}
          >
            {sound.key}
          </button>
        ))}
      </div>
    </div>
  );
}

export default App;

原生audio标签方案补充

如果你想改用原生<audio>标签实现,需要为每个标签绑定src并通过ref控制播放,示例片段:

// 组件内定义ref集合
const audioRefs = useRef<Record<string, HTMLAudioElement | null>>({});

// 播放函数
const handlePlay = (sound: Sound) => {
  setDisplay(sound.name);
  audioRefs.current[sound.key]?.play();
};

// 按钮内的audio标签
<audio 
  ref={el => audioRefs.current[sound.key] = el}
  id={sound.key} 
  src={sound.src}
/>

内容的提问来源于stack exchange,提问作者Wallace Nascimento

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:52:40