开发者问题收集

无法读取未定义的“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个回答

两件事:

  1. 主要问题是渲染时,您正在执行

    onKeyDown={() => this.handleRoshHotkey()}
    

    ...它调用 handleRoshHotkey 而不带任何参数,这就是您收到错误的原因。由于您在构造函数中使用了 bind ,因此将其更改为:

    onKeyDown={this.handleRoshHotkey}
    
  2. 使用 React 中的处理程序就足够了,您 不想通过 addEventListener 使用它,因此删除该代码(我猜您添加它是为了调试)。您可以完全删除 componentDidMountcomponentWillUnmount

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