Keyboard shortcuts in React, the react-hotkeys library
Asked Answered
S

2

6

My goal is to call the setEditing () function in the Todo component. I have created a keyboard shortcut:

const keyMap = {
   TEST: "t"
}; 

const handlers = {
    TEST: () => this.setEditing()
};

const MyHotKeysComponent = withHotKeys (Todo, {keyMap, handlers});

<MyHotKeysComponent>
   <p> press t </p>
</ MyHotKeysComponent>  

In which part of the Todo component do you place these elements?


Code here: https://stackblitz.com/edit/react-cjkf1d?file=index.js

import {  withHotKeys } from "react-hotkeys";

class EditForm extends React.Component {
  render() {
    return (
      <div>
        <textarea onChange={(e) => this.props.handleDescription(e)} value={this.props.description}></textarea>
        <button onClick={this.props.onSave} type="submit">Save</button>
        <button onClick={this.props.onCancel} type="submit">Cancel</button>
      </div>
    )
  }
}

class Todo extends Component {
  constructor(props) {
    super(props);

      this.state = {
        isEditing: false
      }
  }




  setEditing = () => {
    this.setState({
      isEditing: !this.state.isEditing
    })
  }


  render() {
      const { hotKeys, ...remainingProps } = this.props;


    return (
      <div {...{ ...hotKeys, ...remainingProps }}>
        {this.state.isEditing

          ? (<EditForm
            handleChange={this.handleChange}
          />)
          : (
            <li>
              <div>
                {this.props.todo.date}
              </div>
              <div>
                {this.props.todo.description}
              </div>

              <button onClick={() => this.setEditing()}>Edit</button>

            </li>
          )

        }
      </div>
    )
  }
}



    const keyMap = {
      TEST: "t"
    };

    const handlers = {
      TEST: () => this.setEditing()
    };

    const MyHotKeysComponent = withHotKeys(Todo, { keyMap, handlers });

    <MyHotKeysComponent>
      <p>press t</p>
    </MyHotKeysComponent>


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

    this.state = {

      todos: [
        {
          date: '2019-12-09',
          description: 'Hello'
        }
      ],
    };
  }


  render() {
    return (
      <div>
        <ul>
          {
            this.state.todos
              .map((todo, index) =>
                <Todo
                  key={index}
                  index={index}
                  todo={todo}
                />
              )
          }
        </ul>
      </div>
    );
  }
}
Symbolize answered 30/8, 2019 at 0:34 Comment(0)
J
8

You can use HotKeys instead of withHotKeys to handle the event of the component.

I have created small demo for you to handle the event key press.

import { HotKeys } from "react-hotkeys";
import React, { Component } from 'react';
import { render } from 'react-dom';

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isEditing: true
    }

    this.keyMap = {
      TEST: "t"
    };

    this.handlers = {
      TEST: (e) => {
        this.setEditing();
      }
    };
  }

  setEditing = () => {
    this.setState({
      isEditing: !this.state.isEditing
    })
  }

  render() {
    return (
      <HotKeys keyMap={this.keyMap} handlers={this.handlers} >
        <span>My HotKeys are effective here</span><br />
        <b>isEditing: {this.state.isEditing.toString()}</b><br />

        {this.props.children}
      </HotKeys>
    );
  }
}

render(<MyComponent />, document.getElementById('root'));


Keyboard shortcuts in React, the react-hotkeys library

Updated Code: https://stackblitz.com/edit/react-hotkeys-demo?embed=1&file=index.js

I have updated your code and it's working as expected.

import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import { HotKeys } from "react-hotkeys";

class EditForm extends React.Component {
  render() {
    return (
      <div>
        <textarea onChange={(e) => this.props.handleDescription(e)} value={this.props.description}></textarea>
        <button onClick={this.props.onSave} type="submit">Save</button>
        <button onClick={this.props.onCancel} type="submit">Cancel</button>
      </div>
    )
  }
}

class Todo extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isEditing: false
    }

    this.keyMap = {
      TEST: "t"
    };

    this.handlers = {
      TEST: () => this.setEditing()
    };
  }

  setEditing = () => {
    this.setState({
      isEditing: !this.state.isEditing
    })
  }

  render() {
    return (
      <HotKeys keyMap={this.keyMap} handlers={this.handlers} >
        {this.state.isEditing ?
          <EditForm handleChange={this.handleChange} />
          : <li>
            <div>
              {this.props.todo.date}
            </div>
            <div>
              {this.props.todo.description}
            </div>
            <button onClick={() => this.setEditing()}>Edit</button>
          </li>
        }
      </HotKeys>
    )
  }
}


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

    this.state = {

      todos: [
        {
          date: '2019-12-09',
          description: 'Hello'
        }
      ],
    };
  }

  render() {
    return (
      <div>
        <ul>
          {this.state.todos.map((todo, index) =>
            <Todo
              key={index}
              index={index}
              todo={todo}
            />
          )}
        </ul>
      </div>
    );
  }
}

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

Hope this will help to you!

Jar answered 1/9, 2019 at 5:59 Comment(10)
I pressed t but nothing happens in your demoSymbolize
I have noticed now that I have to click on the component first, then press t. And you can't do it to work without selecting a component? I open the demo, press t--> function this.setEditing() calls.Symbolize
That's the behavior of the react-hotkeys npm library. So you have to select the component. In alternative way might be you can auto select the component onload.Jar
How can auto select the component onload? Can you explain me?Symbolize
By using ref you can do it. Set ref="divFocus" to div and set focus by this command ReactDOM.findDOMNode(this.refs.divFocus).focus();Jar
Where put ReactDOM.findDOMNode(this.refs.divFocus).focus();? I did it this way: stackblitz.com/edit/react-vvumtfSymbolize
I have updated the source code for you. Can you please look into that. stackblitz.com/edit/react-hotkeys-demo?file=index.jsJar
If you can resolved the issue then can you please mark an answer as accepted and vote up? So it will helpful for other developer as well.Jar
When I open the demo, I click indifferently where this focus disappears. Can it be done so that it does not disappear? Someone accidentally clicks and the shortcut won't work.Symbolize
Yes you are right. But that's the limitations of the react-hotkeys library.Jar
L
3

My goal is to call the setEditing () function in the Todo component.

You can call setEditing function on keyPress without using react-hotkeys.

I used React's onKeyDown keyboard event to catch user's key presses.

onKeyDown={(e) => this.handleKeyPress(e)}

In handleKeyPress() function, I check the keyCode to determine which key was pressed by user. KeyCode of t is 84. If keyCode is equal to 84, invoke setEditing function. Like this:

  handleKeyPress = e => {
    e.keyCode === 84 &&
      this.setEditing();
  }

Also, you can use a ref to focus to the div, which is the target element for the key press event. So, you don't have to click on it before pressing t.

Demo is here: https://react-pgr9pt.stackblitz.io

Code is here: https://stackblitz.com/edit/react-pgr9pt

Loughlin answered 2/9, 2019 at 15:56 Comment(4)
e.key === 't'?Calvincalvina
@Loughlin When I open the demo, I click indifferently where this focus disappears. Can it be done so that it does not disappear? Someone accidentally clicks and the shortcut won't work.Symbolize
@Symbolize this will not work for you because it will also making trouble to write the text in input. Try to write "test" word in input area and see this will not work. So you can not bind the event with document keydown.Jar
!this.state.isEditing condition can be added after e.keyCode === 84 to overcome that. @ClueMediatorLoughlin

© 2022 - 2024 — McMap. All rights reserved.