onKeyDown React event does not fire in Atom package


#1

I’m trying to use React to render a package view. One problem I’m having is that the onKeyDown event handler in an input field never fires. Using onKeyPress instead does work, but I need the onKeyDown event to work.

I can get the ReactDOM onKeyDown event to work in a React app that’s not part of an Atom package, and I can get the DOM keydown event to work in an Atom package when not using React. For some reason, when using React and Atom together, the onKeyDown event does not fire.

How can I get the React onKeyDown event to fire in an Atom package?

'use babel';

import React from 'react';
import ReactDOM from 'react-dom';

class Panel extends React.Component {
  constructor(props) {
    super(props);
    this.ref = React.createRef();
  }

  componentDidMount() {
    console.log('componentDidMount');
    this.ref.current.value = 'Enter text';
  }

  render() {

    /* In the following code neither onKeyDown nor onKeyUp generate a console log;
       however, using onKeyPress works correctly */
    return (
      <div>
        <div className="message">
          { 'The KeydownIssueView package is Alive! It\'s ALIVE!' }
        </div>
        <input ref={this.ref}
               type="text"
               id="inputId"
               tabIndex="1"
               className="native-key-bindings"
               onKeyDown={ (e) => console.log('onKeyDown', e.nativeEvent) }
               />
      </div>
    );
  }
}

export default class KeydownIssueView {

  constructor(serializedState) {
    this.element = document.createElement('div');
    this.element.classList.add('keydown-issue');

    /* The following code, when un-commented and used without React, works.

    const message = document.createElement('div');
    message.textContent = 'The KeydownIssue package is Alive! It\'s ALIVE!';
    message.classList.add('message');
    this.element.appendChild(message);

    const input = document.createElement('input');
    input.addEventListener('keydown', (e) => console.log('keydown', e))
    this.element.appendChild(input);

    */

    ReactDOM.render(<Panel />, this.element);
  }

  serialize() {}

  destroy() { this.element.remove(); }

  getElement() { return this.element; }

}
'use babel';

import KeydownIssueView from './keydown-issue-view';
import { CompositeDisposable } from 'atom';

export default {

  keydownIssueView: null,
  panel: null,
  subscriptions: null,

  activate(state) {
    this.keydownIssueView = new KeydownIssueView(state.keydownIssueViewState);

    this.panel = atom.workspace.addBottomPanel({
      item: this.keydownIssueView.getElement(),
      visible: false
    });

    this.subscriptions = new CompositeDisposable();

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'keydown-issue:toggle': () => this.toggle()
    }));
  },

  deactivate() {
    this.panel.destroy();
    this.subscriptions.dispose();
    this.keydownIssueView.destroy();
  },

  serialize() {
    return {
      keydownIssueViewState: this.keydownIssueView.serialize()
    };
  },

  toggle() {
    return (
      this.panel.isVisible() ?
      this.panel.hide() :
      this.panel.show()
    );
  }

};