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

React父子组件传递setDisplayClipName后无法更新状态的问题求助

无法更新React状态displayClipName的解决方案

问题现象

已将状态的settersetDisplayClipName从父组件App传递给子组件Pad,但触发声音后displayClipName状态始终无法更新,控制台打印setDisplayClipName显示为bound dispatchSetState(这是React状态更新函数的正常标识,本身无问题)。

代码问题分析

  1. 错误获取clipName:在playSound函数中,尝试通过document.getElementById(clip.id)获取元素,但组件返回的DOM结构里并没有id为clip.id的元素,导致clipName为null,设置状态为null自然看不到界面变化。实际上我们需要的是clip.id这个字符串值,而非DOM元素。
  2. useEffect依赖缺失:监听键盘事件的useEffect依赖数组为空,导致handleKey函数形成闭包,始终引用组件挂载时的clip和setDisplayClipName旧值,键盘触发时可能无法正确调用最新的状态更新函数。
  3. 直接操作DOM不符合React规范:通过document.getElementById(clip.keyTrigger)获取audio元素,应该改用React的useRef来获取DOM引用,避免直接操作DOM。

修正后的代码

const audioClips = [
  {
    keyCode: 67,
    keyTrigger: "C",
    id: "Closed-HH",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3"
  }
];

function App() {
  const [displayClipName, setDisplayClipName] = React.useState('Click a key!');  

  return (
    <div id="drum-machine" className="text-white text-center">
      <div className="container bg-info">
        <h1>FCC - Drum Machine</h1>
        {audioClips.map((clip) => (
          <Pad 
            key={clip.id}
            clip={clip}
            setDisplayClipName={setDisplayClipName}                
          />
        ))}
        <h2>{displayClipName}</h2>
      </div>
    </div>
  );
}

const Pad = ({clip, setDisplayClipName}) => {
  const [playing, setPlaying] = React.useState(false);
  const audioRef = React.useRef(null); // 使用ref获取audio元素

  React.useEffect(() => {
    const handleKey = (e) => {
      if(e.keyCode === clip.keyCode) {
        playSound();
      }
    };
    document.addEventListener('keydown', handleKey);
    return () => {
      document.removeEventListener('keydown', handleKey);
    };
  }, [clip, setDisplayClipName]); // 添加依赖,确保获取最新值

  const playSound = () => {
    setPlaying(true);
    setTimeout(() => setPlaying(false), 300);
    audioRef.current.currentTime = 0; 
    audioRef.current.play();
    setDisplayClipName(clip.id); // 直接传入clip的id字符串
  };

  return (
    <div onClick={playSound} id={`drum-pad-${clip.keyTrigger}`}>
      <audio ref={audioRef} src={clip.src} className="clip" id={clip.keyTrigger}/>
      {clip.keyTrigger}
    </div>
  );
};

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);

关键修改点

  • 移除了无效的DOM元素获取,直接使用clip.id作为状态更新的值
  • 给useEffect添加了正确的依赖数组[clip, setDisplayClipName],避免闭包问题
  • 使用useRef获取audio元素,替代直接操作DOM的方式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 20:31:07