Bagaimana mendeteksi Esc Key Press di React dan bagaimana menanganinya

126

Bagaimana cara mendeteksi penekanan tombol Esc di reactjs? Hal yang mirip dengan jquery

$(document).keyup(function(e) {
     if (e.keyCode == 27) { // escape key maps to keycode `27`
        // <DO YOUR WORK HERE>
    }
});

Setelah terdeteksi saya ingin meneruskan info ke komponen. Saya memiliki 3 komponen yang komponen aktif terakhirnya perlu bereaksi terhadap tombol escape.

Saya sedang memikirkan semacam mendaftar ketika sebuah komponen menjadi aktif

class Layout extends React.Component {
  onActive(escFunction){
    this.escFunction = escFunction;
  }
  onEscPress(){
   if(_.isFunction(this.escFunction)){
      this.escFunction()
   }
  }
  render(){
    return (
      <div class="root">
        <ActionPanel onActive={this.onActive.bind(this)}/>
        <DataPanel onActive={this.onActive.bind(this)}/>
        <ResultPanel onActive={this.onActive.bind(this)}/>
      </div>
    )
  }
}

dan di semua komponen

class ActionPanel extends React.Component {
  escFunction(){
   //Do whatever when esc is pressed
  }
  onActive(){
    this.props.onActive(this.escFunction.bind(this));
  }
  render(){
    return (   
      <input onKeyDown={this.onActive.bind(this)}/>
    )
  }
}

Saya percaya ini akan berhasil tetapi saya pikir ini akan lebih seperti panggilan balik. Apakah ada cara yang lebih baik untuk menangani ini?

Neo
sumber
Apakah Anda tidak memiliki implementasi fluks untuk menyimpan yang merupakan komponen aktif daripada melakukannya dengan cara ini?
Ben Hare
@ BenHare: Saya masih cukup baru untuk itu. Saya mencari kemungkinan untuk bermigrasi ke React. Saya belum mencoba fluks, jadi Anda menyarankan agar saya melihat fluks untuk solusinya. PS: Bagaimana dengan mendeteksi penekanan tombol ESC?
Neo
5
Hanya untuk memperjelas, apakah Anda mencari deteksi penekanan tombol pada tingkat dokumen, seperti blok kode pertama? Atau apakah ini untuk bidang masukan? Anda bisa melakukannya, saya hanya tidak yakin bagaimana membingkai jawaban. Berikut ini adalah contoh tingkat dokumen singkat: codepen.io/anon/pen/ZOzaPW
Brad Colthurst
Tingkat dokumen akan baik-baik saja :)
Neo

Jawaban:

229

Jika Anda mencari penanganan peristiwa kunci tingkat dokumen, maka mengikatnya selama componentDidMountadalah cara terbaik (seperti yang ditunjukkan oleh contoh codepen Brad Colthurst ):

class ActionPanel extends React.Component {
  constructor(props){
    super(props);
    this.escFunction = this.escFunction.bind(this);
  }
  escFunction(event){
    if(event.keyCode === 27) {
      //Do whatever when esc is pressed
    }
  }
  componentDidMount(){
    document.addEventListener("keydown", this.escFunction, false);
  }
  componentWillUnmount(){
    document.removeEventListener("keydown", this.escFunction, false);
  }
  render(){
    return (   
      <input/>
    )
  }
}

Perhatikan bahwa Anda harus memastikan untuk menghapus key event listener saat unmount untuk mencegah potensi kesalahan dan kebocoran memori.

EDIT: Jika Anda menggunakan kait, Anda dapat menggunakan useEffectstruktur ini untuk menghasilkan efek yang serupa:

const ActionPanel = (props) => {
  const escFunction = useCallback((event) => {
    if(event.keyCode === 27) {
      //Do whatever when esc is pressed
    }
  }, []);

  useEffect(() => {
    document.addEventListener("keydown", escFunction, false);

    return () => {
      document.removeEventListener("keydown", escFunction, false);
    };
  }, []);

  return (   
    <input />
  )
};
Chase Sandmann
sumber
7
Apa argumen ketiga dalam addEventListenerdan removeEventListener?
jhuang
2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/… - bisa otions ou useCapture
Julio Saito
2
@jhuang itu adalah argumen useCapture yang menentukan apakah akan menangkap peristiwa yang biasanya tidak menggelembung atau tidakfocus . Ini tidak relevan untuk keydown, tetapi dulunya tidak opsional sehingga biasanya ditentukan untuk kompatibilitas dengan IE <9.
Chase Sandmann
Saya harus membungkus this.escFunctionke dalam fungsi seperti function() { this.escFunction(event) }, jika tidak itu dijalankan pada pemuatan halaman tanpa "keydown".
M3RS
2
Tidak perlu menyertakan 'false' sebagai argumen ke-3. Itu salah secara default.
NattyC
25

Anda akan ingin mendengarkan escape keyCode(27) dari ReactSyntheticKeyBoardEvent onKeyDown :

const EscapeListen = React.createClass({
  handleKeyDown: function(e) {
    if (e.keyCode === 27) {
      console.log('You pressed the escape key!')
    }
  },

  render: function() {
    return (
      <input type='text'
             onKeyDown={this.handleKeyDown} />
    )
  }
})

CodePen Brad Colthurst yang diposting di komentar pertanyaan berguna untuk menemukan kode kunci untuk kunci lain.

pirt
sumber
15
Saya rasa ada baiknya menambahkan bahwa React tampaknya tidak mengaktifkan onKeyPressevent ketika tombol yang ditekan adalah ESC dan fokus ada di dalam <input type="text" />. Dalam skenario itu, Anda dapat menggunakan onKeyDowndan onKeyUp, keduanya menembak, dan dalam urutan yang benar.
Tom
12

Cara lain untuk melakukannya dalam komponen fungsional, adalah dengan menggunakan useEffectdan useFunction, seperti ini:

import React, { useEffect } from 'react';

const App = () => {


  useEffect(() => {
    const handleEsc = (event) => {
       if (event.keyCode === 27) {
        console.log('Close')
      }
    };
    window.addEventListener('keydown', handleEsc);

    return () => {
      window.removeEventListener('keydown', handleEsc);
    };
  }, []);

  return(<p>Press ESC to console log "Close"</p>);
}

Alih-alih console.log, Anda dapat menggunakan useStateuntuk memicu sesuatu.

Jonas Sandstedt
sumber
7

React menggunakan SyntheticKeyboardEvent untuk membungkus event browser asli dan event Synthetic ini menyediakan atribut key bernama ,
yang bisa Anda gunakan seperti ini:

handleOnKeyDown = (e) => {
  if (['Enter', 'ArrowRight', 'Tab'].includes(e.key)) {
    // select item
    e.preventDefault();
  } else if (e.key === 'ArrowUp') {
    // go to top item
    e.preventDefault();
  } else if (e.key === 'ArrowDown') {
    // go to bottom item
    e.preventDefault();
  } else if (e.key === 'Escape') {
    // escape
    e.preventDefault();
  }
};
BitOfUniverse
sumber
3
fakta menarik jika Anda perlu mendukung IE9 dan / atau Firefox 36 atau lebih rendah, e.keyuntuk Escapekembali lagi sebagai Esc#triggered
Agustus
4

Untuk solusi hook React yang dapat digunakan kembali

import React, { useEffect } from 'react';

const useEscape = (onEscape) => {
    useEffect(() => {
        const handleEsc = (event) => {
            if (event.keyCode === 27) 
                onEscape();
        };
        window.addEventListener('keydown', handleEsc);

        return () => {
            window.removeEventListener('keydown', handleEsc);
        };
    }, []);
}

export default useEscape

Pemakaian:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))
Bjorn Reppen
sumber
3
Solusinya tidak memungkinkan untuk mengubah fungsi onEscape. Anda harus useMemo dan meneruskan onEscape sebagai dep array untuk useEffect
Neo