Cách phát hiện Esc Key Press trong React và cách xử lý nó


126

Làm cách nào để phát hiện phím Esc trên reactjs? Điều tương tự với jquery

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

Sau khi phát hiện, tôi muốn chuyển các thành phần thông tin xuống. Tôi có 3 thành phần trong đó thành phần hoạt động cuối cùng cần phản ứng với phím thoát.

Tôi đã nghĩ đến một kiểu đăng ký khi một thành phần hoạt động

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>
    )
  }
}

và trên tất cả các thành phần

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)}/>
    )
  }
}

Tôi tin rằng điều này sẽ hiệu quả nhưng tôi nghĩ nó sẽ giống như một cuộc gọi lại hơn. Có cách nào tốt hơn để xử lý điều này không?


Bạn không có triển khai thông lượng nào để lưu trữ thành phần đang hoạt động thay vì làm theo cách này?
Ben Hare

@BenHare: Tôi vẫn còn khá mới với nó. Tôi đang xem xét tính khả thi của việc chuyển sang React. Tôi chưa thử thông lượng, vì vậy bạn đề nghị tôi nên xem xét thông lượng để tìm giải pháp. Tái bút: Làm thế nào về việc phát hiện phím ESC?
Neo

5
Chỉ cần làm rõ, bạn đang tìm kiếm tính năng phát hiện nhấn phím ở cấp độ tài liệu, như khối mã đầu tiên? Hay đây là một trường đầu vào? Bạn có thể làm một trong hai, tôi chỉ không chắc làm thế nào để đóng khung một câu trả lời. Đây là cấp tài liệu nhanh, ví dụ: codepen.io/anon/pen/ZOzaPW
Brad Colthurst vào

Mức độ tài liệu sẽ ổn :)
Neo

Câu trả lời:


229

Nếu bạn đang tìm kiếm cách xử lý sự kiện khóa cấp tài liệu, thì liên kết nó trong suốt componentDidMountlà cách tốt nhất (như được hiển thị trong ví dụ codepen của 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/>
    )
  }
}

Lưu ý rằng bạn nên đảm bảo loại bỏ trình xử lý sự kiện quan trọng khi ngắt kết nối để tránh các lỗi tiềm ẩn và rò rỉ bộ nhớ.

CHỈNH SỬA: Nếu bạn đang sử dụng hook, bạn có thể sử dụng useEffectcấu trúc này để tạo ra hiệu ứng tương tự:

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 />
  )
};

7
Đối số thứ ba trong addEventListenervà là removeEventListenergì?
jhuang

2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/... - có thể otions ou useCapture
Julio Saito

2
@jhuang đó là đối số useCapture chỉ định có hay không nắm bắt các sự kiện thường không nổi lên như vậyfocus . Nó không liên quan cho keydown, nhưng nó từng không phải là tùy chọn nên nó thường được chỉ định để tương thích với IE <9.
Chase Sandmann

Tôi phải gói this.escFunctionvào một chức năng như function() { this.escFunction(event) }, nếu không nó đang thực thi khi tải trang mà không có bất kỳ "keydown" nào.
M3RS

2
Không cần bao gồm 'false' làm đối số thứ 3. Nó sai theo mặc định.
NattyC 19/07/19

25

Bạn sẽ muốn nghe thoát keyCode(27) từ 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 của Brad Colthurst được đăng trong phần nhận xét của câu hỏi rất hữu ích cho việc tìm mã khóa cho các khóa khác.


15
Tôi nghĩ rằng điều đáng nói là React dường như không kích hoạt onKeyPresssự kiện khi phím được nhấn là ESC và tiêu điểm nằm bên trong <input type="text" />. Trong trường hợp đó, bạn có thể sử dụng onKeyDownonKeyUp, cả hai đều kích hoạt và theo đúng thứ tự.
Tom,

12

Một cách khác để thực hiện điều này trong một thành phần chức năng, là sử dụng useEffectuseFunction, như sau:

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>);
}

Thay vì console.log, bạn có thể sử dụng useStateđể kích hoạt một cái gì đó.


7

React sử dụng SyntheticKeyboardEvent để bao bọc sự kiện trình duyệt gốc và sự kiện Synthetic này cung cấp thuộc tính khóa được đặt tên ,
bạn có thể sử dụng thuộc tính này như sau:

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();
  }
};

3
vui vẻ thực tế, nếu bạn cần hỗ trợ IE9 và / hoặc Firefox 36 hoặc thấp hơn, e.keycho Escapetrở lại như Esc#triggered
Tháng Tám

4

Đối với giải pháp móc React có thể tái sử dụng

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

Sử dụng:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))

3
Giải pháp không cho phép thay đổi chức năng onEscape. Bạn nên sử dụngMemo và chuyển onEscape làm mảng dep để sử dụngEffect
Neo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.