Simpleton
Simpleton

Reputation: 6415

React not responding to key down event

I'm trying to implement some very basic key press detection and I can't get it to work at all. I have a bare component that should be picking up on the onKeyDown event but nothing gets logged out in the console:

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  handleKeyDown(event) {
    console.log('handling a key press');
  }

  render() {
    return (
      <ChildComponent onKeyDown={this.handleKeyDown} />
    );
  }
}

React.render(<App />, document.getElementById('app'));

Upvotes: 14

Views: 26074

Answers (4)

widged
widged

Reputation: 2779

The problem is that ChildComponent is not a component but a component factory. It will be replaced with the result of rendering the element created with that factory.

Insert the ChildComponent in a div and attach any event listener to the div, not ChildComponent. Replace <div> with <span> if you need inline display.

let {Component} = React;

class ChildComponent extends Component {
  render() {
    return ( <child-component>press down a key</child-component> );
  }
}

class App extends Component {
  handleKeyDown(event) {
    console.log('handling a key press');
  }

  render() {
    return ( <div onKeyDown={this.handleKeyDown}><ChildComponent  /></div> );
  }
}

React.render(<App />, document.getElementById('app'));

See it in action on codepen

Upvotes: 6

Pete TNT
Pete TNT

Reputation: 8403

You'll need to assign a tabIndex-attribute for your element (the wrapping element for example) for it to receive keypresses. Then pass the keyDown handler to it with the props:

import React from 'react';
import { render } from 'react-dom';

class ChildComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div tabIndex="0" onKeyDown={this.props.handleKeyDown}>Fooo</div> 
    );
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  handleKeyDown(event) {
    console.log('handling a key press');
  }

  render() {
    return (
      <ChildComponent handleKeyDown={() => this.handleKeyDown()} />
    );
  }
}

render(<App />, document.getElementById('app')); 

Upvotes: 35

Benny Schmidt
Benny Schmidt

Reputation: 3394

The DOM wants the element to be focused in order to receive the keyboard event. If you don't want to hack the element with tabIndex or contentEditable to get it to focus, you could use native DOM event listeners on window, and define a different handler in each component that handles keyboard events.

Just be sure to remove the event listener when that component unmounts so all components aren't handling all the time:

  componentWillMount: function() {
    window.addEventListener('keydown', this.handleKeyDown);
  },

  componentWillUnmount: function() {
    window.removeEventListener('keydown', this.handleKeyDown);
  },

Also, there appears to be an npm that provides similar functionality if that's easier: https://www.npmjs.com/package/react-keydown

Upvotes: 15

Jeff Fairley
Jeff Fairley

Reputation: 8314

In an app my team is working on, we're using react-hotkey. Unfortunately, React doesn't seem to support mixins with ES6 syntax, but if you're cool with some babel, you could give it a try.

let App = React.createClass({
  mixins: [hotkey.Mixin('handleKeyDown')],

  componentDidMount() {
    hotkey.activate();
  },

  componentWillUnmount() {
    hotkey.disable();
  },

  handleKeyDown(event) {
    console.log('handling a key press');
  },

  render() {
    return (
      <ChildComponent />
    );
  }
});

React.render(<App />, document.getElementById('app'));

Upvotes: 0

Related Questions