如何使用React Hooks将映射的onkeypress函数绑定到音频元素



我正在使用Hooks重构以前制作的基于类的React应用程序,使其能够正常工作。这是一个简单的鼓机应用程序。我正在使用.map从一组对象中填充页面,这些对象包含reach drum pad及其相关声音的数据。

{soundBank.map((sound) => (
<DrumPad
key={sound.id}
id={sound.id}
letter={sound.letter}
src={sound.src}
handleDisplay={handleDisplay}
/>
))}

重构后,除了使用onKeyPress演奏单个鼓盘外,其他一切似乎都能正常工作。以下是我的代码作为基于类的组件的样子:

handleKeyPress = event => {
if (event.keyCode === this.props.letter.charCodeAt()) {
this.audio.play();
this.audio.currentTime = 0;
this.props.handleDisplay(this.props.id);
}
render() {
return (
<div
className="drum-pad"
id={this.props.id}
onClick={this.handleClick.bind(this)}
onKeyPress={this.handleKeyPress.bind(this)}
>
<h1>{this.props.letter}</h1>
<h2>{this.props.id}</h2>
<audio
className="clip"
id={this.props.letter}
src={this.props.src}
ref={ref => (this.audio = ref)}
></audio>
</div>
);}

以下是它作为一个功能组件的外观:

let audio = useRef(null);
const handleKeyPress = (event) => {
if (event.keyCode === props.letter.charCodeAt()) {
audio.play();
audio.currentTime = 0;
props.handleDisplay(props.id);
}
};
return (
<div
className="drum-pad"
id={props.id}
onClick={handleClick}
onKeyPress={handleKeyPress}
>
<h1>{props.letter}</h1>
<h2>{props.id}</h2>
<audio
className="clip"
id={props.letter}
src={props.src}
ref={(ref) => (audio = ref)}
></audio>
</div>);

我尝试过使用useCallback,以及为音频创建状态,但我无法使其工作。当页面加载时,第一次按键会播放声音,但之后任何一次按键,我都会收到错误:

Uncaught TypeError: Cannot read property 'play' of null
at HTMLDocument.handleKeyPress

通过使用console.log测试错误,似乎每当按下某个键时,handleKeyPress函数都会被调用9次(每个鼓垫调用一次(。我如何才能在一次按键时只激活一个特定的鼓垫,以及如何确保音频不会恢复为空?这个问题也可能与我使用参考号有关。我这样做是为了学习Hooks(以及React(,所以任何指向正确方向的指针都将不胜感激。

解决方案

以下是我基于Oleksandr Kovpashko选择的答案的解决方案:

const audio = useRef(null);
const handleKeyPress = useCallback((event) => {
if (event.keyCode === props.letter.charCodeAt()) {
audio.current.play();
audio.current.currentTime = 0;
props.handleDisplay(props.id);
}}, []);
<audio
className="clip"
id={props.letter}
src={props.src}
ref={audio}
></audio>

似乎使用refs的方式不对。

useRef钩子返回一个"ref"对象,您应该将该对象直接传递给<audio>标记的ref道具。然后使用handleKeyPress功能中的audio.current访问音频。此外,您应该将事件处理程序封装到useCallback钩子中,因为当前实现在每个渲染周期中都会为您提供一个新的handleKeyPress函数。

PS:为什么要从handleKeyPress函数中删除返回值中的事件侦听器?没有必要使用React手动管理事件侦听器,除非您使用addEventListener手动添加它们。

我看到3个问题:

当您使用useRef创建一个ref对象时,您可以通过添加.current(例如:audio.current.play(((来访问它

charCodeAt函数需要一个索引,也许这就是handleKeyPress被多次调用的原因

除非在之前调用了addEventListener,否则不要调用removeEventListener

最新更新