Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

adding event listener for keydown to react element

I'm trying to add an event listener for keydown event in an image (or div) tag. It works if I add it to the document with document.addEventListener, but it doesn't when I try to put it into the specific element that I create in react (I noted in the code what works and what doesn't). Also handleClick works and handleKey does not, no matter which format I put it into the tag with.

class PrescriptionImage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      patient: "",
      rotation: 0
    };
    this.handleKey = this.handleKey.bind(this);
  }

  handleClick() {
    this.setState({rotation: this.state.rotation + 270})
  }

  handleKey(e) {
    e.preventDefault();
    console.log(e);
    if (e.code == 'ArrowLeft') {
      if (e.ctrlKey) {
        this.setState({rotation: this.state.rotation + 270})
      }
    }
  }

  componentDidMount() {
//    document.getElementById("left").addEventListener("keydown", this.handleKey, true); This doesn't work (no error)
//    this.RxImage.addEventListener("keydown", this.handleKey, false); This doesn't work, (can't find addEventListener of "undefined")
//    document.addEventListener("keydown", this.handleKey, false); This works.
    fetch("http://localhost:3333/patientAddress.json")
      .then(res => res.json())
      .then(
        result => {
          this.setState({
            isLoaded: true,
            patient: result.order.patient
          });
        },
        error => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      );
  }

  componentWillUnmount(){
    document.removeEventListener("keydown", this.handleKey, false);
  }

  render() {
    const { error, isLoaded, patient, rotation } = this.state;
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
      return <div>Loading...</div>;
    } else {
      return <img className="prescription-image" style={{width: "98%", height: "98%", transform: `rotate(${rotation}deg)`}} src={"data:image/png;base64," + patient.rx.imageData} onClick={() => this.handleClick()} onKeyDown={this.handleKey} />;
    }
  }
}

ReactDOM.render(<PrescriptionImage />, document.getElementById("left"));
like image 527
xyious Avatar asked Dec 21 '17 21:12

xyious


2 Answers

You got 2 main issues here:

  1. The keyDown event needs the div to be in focus. one way to do it is to add a tabindex attribute to the div. after you focus on it you can trigger the onKeyDown event on any key on the keyboard.
  2. In your handler you are trying to check for e.code but in fact the correct property is e.keycode.
    with that said, you should carefully read about the browsers support for it as it is considered as deprecated at some browsers.
    Here is a list of the available properties of this event and their status (check key for example).

EDIT
I have added another approach, using the ref API of react. This way you can attach an event listener the way you did before and also trigger a focus via code (see componentDidMount).

Here is a running example:

class App extends React.Component {

  componentDidMount() {
    this.myDiv.addEventListener('keydown', this.handleKey);
    this.myDiv.focus();
  }

  componentWillUnmount() {
    this.myDiv.removeEventListener('keydown', this.handleKey);
  }

  handleKey = e => {
    console.log(e.keyCode);
  }

  render() {
    return (
      <div>
        <div tabIndex="0" onKeyDown={this.handleKey}>click me</div>
        <div tabIndex="1" ref={ref => this.myDiv = ref}>by ref</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
like image 74
Sagiv b.g Avatar answered Nov 15 '22 00:11

Sagiv b.g


This implementation worked well for me.

class App extends React.Component {

  constructor(props) {
    super(props);
    this.myDiv = React.createRef();
  }

  componentDidMount() {
    this.myDiv.current.addEventListener('keydown', this.handleKey);
    this.myDiv.current.focus();
  }

  componentWillUnmount() {
    this.myDiv.current.removeEventListener('keydown', this.handleKey);
  }

  handleKey = e => {
    console.log(e.keyCode);
  }

  render() {
    return (
      <div>
        <div tabIndex="0" onKeyDown={this.handleKey}>click me</div>
        <div tabIndex="1" ref={this.myDiv}>by ref</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
like image 43
Sergio Barbosa Avatar answered Nov 14 '22 22:11

Sergio Barbosa