无法读取未定义的“keys”属性,处理 React 中的事件
2021-04-21
956
我的目标是有一个按钮,可以运行 Javascript
roshtimer()
,并且还有可用于运行命令的热键。例如,我希望用户可以选择单击屏幕上的按钮或按键盘上的“r”。
我在这个问题上遇到了不少麻烦,因为我对 React 还比较陌生。
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
time: 0,
roshDead: false,
roshDeathTime: 0,
aegisExpireTime: 0,
roshRespawnTime1: 0,
roshRespawnTime2: 0,
start: 0,
isOn: false,
};
this.startTimer = this.startTimer.bind(this);
this.stopTimer = this.stopTimer.bind(this);
this.resetTimer = this.resetTimer.bind(this);
this.roshTimer = this.roshTimer.bind(this);
this.roshButton = React.createRef();
this.handleRoshHotkey = this.handleRoshHotkey.bind(this);
}
componentDidMount() {
document.addEventListener("keydown", this.handleRoshHotkey);
}
componentWillUnmount() {
document.removeEventListener("keydown", this.handleRoshHotkey);
}
handleRoshClick() {
console.log("button was clicked!");
this.roshTimer();
}
handleRoshHotkey = (e) => {
if (e.key === "r" || e.keyCode === 13) {
console.log("hotkey was pressed!");
this.roshTimer();
}
};
startTimer() {
...
}
stopTimer() {
...
}
resetTimer() {
...
}
roshTimer() {
//compute rosh timers
let roshDeathTime = this.state.time;
let newAegisExpire = this.state.time + 300000; //5 mins = 300000 milliseconds
let newRespTime1 = this.state.time + 480000; // 8 mins = 480000 milliseconds
let newRespTime2 = this.state.time + 660000; // 11 mins = 660000 milliseconds
this.setState({
roshDeathTime: this.state.time,
aegisExpireTime: newAegisExpire,
roshRespawnTime1: newRespTime1,
roshRespawnTime2: newRespTime2,
roshDead: true,
});
//format rosh timers
...
//log to console for my own sanity
console.log(roshDeathTime, newAegisExpire, newRespTime1, newRespTime2);
//copy to user's clipboard
navigator.clipboard.writeText(
newAegisExpire + " " + newRespTime1 + " " + newRespTime2
);
}
render() {
let start =
this.state.time == 0 ? (
<button onClick={this.startTimer}>start</button>
) : null;
let stop = this.state.isOn ? (
<button onClick={this.stopTimer}>stop</button>
) : null;
let reset =
this.state.time != 0 && !this.state.isOn ? (
<button class="red" onClick={this.resetTimer}>
reset
</button>
) : null;
let resume =
this.state.time != 0 && !this.state.isOn ? (
<button class="green" onClick={this.startTimer}>
resume
</button>
) : null;
let rosh = this.state.isOn ? (
<div className={this.state.focused ? "focused" : ""}>
<button
onClick={() => this.handleRoshClick()}
autoFocus
ref={(c) => (this._input = c)}
>
{" "}
Rosh died!{" "}
</button>
</div>
) : null;
return (
<div>
<h3>
Timer: {prettyMilliseconds(this.state.time, { colonNotation: true })}
</h3>
{start}
{resume}
{stop}
{reset}
<div ref={this.roshButton} autofocus onKeyDown={() => this.handleRoshHotkey()} tabIndex={1}>
{rosh}
</div>
</div>
);
}
}
export default Timer;
目前,我可以运行我的应用程序并发送“Rosh died!”按钮并查看预期的输出。如果我单击网页上的任意位置,则可以使用热键来获取预期的输出。但是,如果我在手动单击按钮之前使用热键,应用程序就会崩溃,并且我会收到
TypeError: Cannot read property 'key' of undefined
进一步说明我的问题在这里:
handleRoshHotkey = (e) => {
if (e.key === "r" || e.keyCode === 13){ <-------------
console.log("hotkey was pressed!");
this.roshTimer();
}
};
这是崩溃前的示例控制台日志:
button was clicked!
1134 "5:01" "8:01" "11:01"
button was clicked!
1878 "5:01" "8:01" "11:01"
hotkey as pressed!
4318 "5:04" "8:04" "11:04"
hotkey was pressed!
5338 "5:05" "8:05" "11:05"
button was clicked!
8142 "5:08" "8:08" "11:08"
Uncaught TypeError: Cannot read property 'key' of undefined
.
. //(hundreds of lines of traceback follow)
.
hotkey was pressed!
11194 "5:11" "8:11" "11:11"
谢谢!
3个回答
两件事:
-
主要问题是渲染时,您正在执行
onKeyDown={() => this.handleRoshHotkey()}
...它调用
handleRoshHotkey
而不带任何参数,这就是您收到错误的原因。由于您在构造函数中使用了bind
,因此将其更改为:onKeyDown={this.handleRoshHotkey}
-
使用 React 中的处理程序就足够了,您 也 不想通过
addEventListener
使用它,因此删除该代码(我猜您添加它是为了调试)。您可以完全删除componentDidMount
和componentWillUnmount
。
T.J. Crowder
2021-04-21
您忘记传递事件:
<div ref={this.roshButton} autofocus onKeyDown={(e) => this.handleRoshHotkey(e)} tabIndex={1}>
{rosh}
</div>
但更好:
<div ref={this.roshButton} autofocus onKeyDown={this.handleRoshHotkey} tabIndex={1}>
{rosh}
</div>
FireFighter
2021-04-21
这可能应该只是一条注释,但您没有在
onKeyDown={() => this.handleRoshHotkey()>
中传递事件。
由于该函数已绑定,只需传递
onKeyDown={this.handleRoshHotkey>
,事件就会被管道传输。
lux
2021-04-21