วิธีตรวจจับ Esc Key Press ใน React และวิธีจัดการ


126

ฉันจะตรวจจับการกดแป้น Esc บน reactjs ได้อย่างไร สิ่งที่คล้ายกับ jquery

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

เมื่อตรวจพบแล้วฉันต้องการส่งต่อข้อมูลลงส่วนประกอบ ฉันมีส่วนประกอบ 3 ส่วนซึ่งส่วนประกอบที่ใช้งานสุดท้ายต้องตอบสนองต่อการกดแป้น Escape

ฉันกำลังคิดถึงการลงทะเบียนประเภทหนึ่งเมื่อส่วนประกอบเริ่มทำงาน

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

และส่วนประกอบทั้งหมด

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

ฉันเชื่อว่าสิ่งนี้จะได้ผล แต่ฉันคิดว่ามันจะเหมือนการโทรกลับมากกว่า มีวิธีใดที่ดีกว่านี้ในการจัดการกับปัญหานี้หรือไม่?


คุณไม่มีการใช้งานฟลักซ์เพื่อจัดเก็บซึ่งเป็นส่วนประกอบที่ใช้งานอยู่แทนที่จะทำด้วยวิธีนี้หรือไม่?
Ben Hare

@ BenHare: ฉันยังใหม่อยู่ ฉันกำลังตรวจสอบความเป็นไปได้ในการย้ายไปสู่ ​​React ฉันไม่ได้ลองใช้ฟลักซ์ดังนั้นคุณจึงแนะนำว่าฉันควรตรวจสอบฟลักซ์เพื่อหาวิธีแก้ปัญหา PS: วิธีการตรวจจับการกดแป้น ESC?
นีโอ

5
เพื่อชี้แจงคุณกำลังมองหาการตรวจจับการกดแป้นพิมพ์ในระดับเอกสารเช่นบล็อกรหัสแรกหรือไม่? หรือนี่คือช่องป้อนข้อมูล? คุณสามารถทำได้ฉันไม่แน่ใจว่าจะกำหนดคำตอบอย่างไร นี่คือระดับเอกสารด่วนเช่นcodepen.io/anon/pen/ZOzaPW
Brad Colthurst

ระดับเอกสารจะดี :)
นีโอ

คำตอบ:


229

หากคุณกำลังมองหาการจัดการเหตุการณ์สำคัญระดับเอกสารการเชื่อมโยงระหว่างcomponentDidMountนั้นเป็นวิธีที่ดีที่สุด (ดังที่แสดงในตัวอย่าง 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/>
    )
  }
}

โปรดทราบว่าคุณควรตรวจสอบให้แน่ใจว่าได้ลบตัวฟังเหตุการณ์สำคัญในการยกเลิกการต่อเชื่อมเพื่อป้องกันข้อผิดพลาดที่อาจเกิดขึ้นและการรั่วไหลของหน่วยความจำ

แก้ไข: หากคุณใช้ตะขอคุณสามารถใช้useEffectโครงสร้างนี้เพื่อสร้างเอฟเฟกต์ที่คล้ายกัน:

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
อาร์กิวเมนต์ที่สามในaddEventListenerและremoveEventListenerคืออะไร?
jhuang

2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/… - สามารถเลือกได้ ou useCapture
Julio Saito

2
@jhuang เป็นอาร์กิวเมนต์ useCapture ซึ่งระบุว่าจะจับภาพเหตุการณ์ที่มักจะไม่เกิดขึ้นfocusหรือไม่ ไม่เกี่ยวข้องkeydownแต่เคยเป็นทางเลือกดังนั้นจึงมักระบุว่าเข้ากันได้กับ IE <9
Chase Sandmann

ฉันต้องรวมthis.escFunctionเข้ากับฟังก์ชันเช่นfunction() { this.escFunction(event) }มิฉะนั้นจะดำเนินการเมื่อโหลดหน้าเว็บโดยไม่มี "คีย์ดาวน์"
M3RS

2
ไม่จำเป็นต้องใส่ 'เท็จ' เป็นอาร์กิวเมนต์ที่ 3 เป็นเท็จโดยค่าเริ่มต้น
NattyC

25

คุณจะต้องการฟัง Escape keyCode(27) จาก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 ที่โพสต์ไว้ในความคิดเห็นของคำถามมีประโยชน์ในการค้นหารหัสหลักสำหรับคีย์อื่น ๆ


15
ฉันคิดว่ามันมูลค่าเพิ่มที่ตอบสนองจะปรากฏขึ้นที่จะไม่ยิงonKeyPressเหตุการณ์เมื่อถูกกดคีย์จะ ESC <input type="text" />และมุ่งเน้นที่อยู่ภายใน ในสถานการณ์ดังกล่าวคุณสามารถใช้onKeyDownและonKeyUpซึ่งทั้งสองจุดเริ่มต้นและเรียงตามลำดับที่ถูกต้อง
ทอม

12

อีกวิธีหนึ่งในการทำสิ่งนี้ให้สำเร็จในองค์ประกอบที่ใช้งานได้คือการใช้useEffectและuseFunctionเช่นนี้:

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

แทนที่จะเป็น console.log คุณสามารถใช้useStateทริกเกอร์บางสิ่งได้


7

ตอบสนองการใช้งานSyntheticKeyboardEventห่อเหตุการณ์เบราว์เซอร์และเหตุการณ์การสังเคราะห์นี้จะให้ชื่อแอตทริบิวต์ที่สำคัญ ,
ซึ่งคุณสามารถใช้เช่นนี้

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
สนุกความเป็นจริงถ้าคุณต้องการที่จะสนับสนุน IE9 และ / หรือ Firefox 36 หรือต่ำกว่าe.keyสำหรับการEscapeกลับมาเป็นEsc#triggered
สิงหาคม

4

สำหรับโซลูชัน React hook ที่ใช้ซ้ำได้

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

การใช้งาน:

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

3
โซลูชันนี้ไม่อนุญาตให้เปลี่ยนฟังก์ชัน onEscape คุณควรใช้ Memo และส่งผ่าน onEscape เป็นอาร์เรย์ของการใช้งาน useEffect
Neo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.