ayrton / react-key-handler

React component to handle keyboard events :key:
http://ayrton.be/react-key-handler
388 stars 29 forks source link

I do not understand the real usage of this lib and its API #141

Closed guillaumepotier closed 6 years ago

guillaumepotier commented 6 years ago

Sorry for the harsh/clumsy title, but why this API?

I mean, If I understand correctly, in the API, here

export default keyHandler({ keyEventName: KEYPRESS, keyValue: 's' })(Demo);

It seems that the component explicitly waits for a specific KEYPRESS to be s in order for the component to have this.props.keyEventName and this.props.keyValue not null (but 115 and s).

Why that choice?

I was more expecting a HOC/decorator that would give me in props any keyboard events with their value, my component having to deal with that specifically in componentWillReceiveProps() or componentWillUpdate(), but this current API seems very limiting.

Unless I'm missing the big picture here :)

Thanks for your lights

neilyoung commented 6 years ago

@guillaumepotier Yes, this was my expectation also. Seems to be not the case

guillaumepotier commented 6 years ago

Inedeed, FYI I went on that handmade solution:

import React, { Component } from 'react'
import keyboardJS from 'keyboardjs'
import moment from 'moment'

export function withKeyboard (WrappedComponent) {
  return class Keyboard extends Component {
    static displayName = `WithKeyboard(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`

    static getClass = () => (WrappedComponent.getClass ? WrappedComponent.getClass() : WrappedComponent)

    constructor (props) {
      super(props)

      this.isPressed = this.isPressed.bind(this)
      this.handleKeypress = this.handleKeypress.bind(this)

      this.state = {
        pressedKeys: [],
        isTrusted: false,
        isPressed: this.isPressed,
        at: null
      }
    }

    componentDidMount () {
      keyboardJS.bind('', this.handleKeypress)
    }

    handleKeypress ({ isTrusted, pressedKeys }) {
      // at precision is 0,1 sec
      this.setState({ isTrusted, pressedKeys, at: Math.floor(moment().valueOf() / 100) })
    }

    // string could be 'a', 'b', 'b + c', 'command+shift+a', etc..
    isPressed (string) {
      const keys = string
        .replace(/\s/g, '') // remove all spaces
        .split('+') // split by `+` char

      for (let i = 0; i < keys.length; i++) {
        if (this.state.pressedKeys.indexOf(keys[i]) === -1) {
          return false
        }
      }

      return true
    }

    componentWillUnmount () {
      keyboardJS.unbind('', this.handleKeypress)
    }

    render () {
      return <WrappedComponent {...this.props} keyboard={this.state} />
    }
  }
}

That way in my components I use that like this:

  componentWillUpdate ({ messages, keyboard }) {
    if (keyboard.at !== this.props.keyboard.at && keyboard.isPressed('ctrl + m')) {
      this.setState({ hiddenMenuPanel: !this.state.hiddenMenuPanel })
    }
  }

Did not find something better than keyboard.at with a 100ms tresshold to avoid inifinite loops.. Kinda ugly, but for now on, its does the job

neilyoung commented 6 years ago

@guillaumepotier Thanks for sharing!