Reajjs में दस्तावेज़ के लिए कीपर सुनें


86

मैं escapeप्रेस पर सक्रिय प्रतिक्रिया बूटस्ट्रैप पॉपओवर को बंद करना चाहता हूं। यह कोड है

_handleEscKey:function(event){
         console.log(event);
        if(event.keyCode == 27){
          this.state.activePopover.hide();
        }
   },

  componentWillMount:function(){
     BannerDataStore.addChangeListener(this._onchange);
     document.addEventListener("click", this._handleDocumentClick, false);
     document.addEventListener("keyPress", this._handleEscKey, false);
   },


   componentWillUnmount: function() {
     BannerDataStore.removeChangeListener(this._onchange);
      document.removeEventListener("click", this._handleDocumentClick, false);
      document.removeEventListener("keyPress", this._handleEscKey, false);
   },

लेकिन किसी भी कुंजी को दबाते समय कंसोल में कुछ भी लॉग इन नहीं हो रहा है। मैंने यह भी सुनने की कोशिश की है कि खिड़की पर भी और विभिन्न मामलों के साथ।'keypress ',' keyup 'आदि लेकिन ऐसा लगता है कि मैं कुछ गलत कर रहा हूं।


इसके लायक मैंने रिएक्ट के लिए एक कीडाउन लिब प्रकाशित किया है, जो इस सब को बहुत आसान बनाने के लिए है: github.com/jedverity/react-keydown
glortho

जवाबों:


63

आपको उपयोग करना चाहिए keydownऔर नहीं keypress

Keypress (पदावनत) आमतौर पर केवल उन कुंजियों के लिए उपयोग किया जाता है जो डॉक्स के अनुसार कैरेक्टर आउटपुट का उत्पादन करते हैं

कुंजीपट (पदावनत)

जब एक कुंजी को दबाया जाता है तो कीपर घटना को निकाल दिया जाता है और यह कुंजी सामान्य रूप से एक चरित्र मान उत्पन्न करती है

चाबी नीचे

जब एक कुंजी को दबाया जाता है, तो कीडाउन इवेंट को निकाल दिया जाता है।


1
कीप को पदावनत कर दिया गया है।
टाइमपैराडॉक्स

52

बस खुद के साथ भी ऐसी ही समस्या थी। मैं आपके कोड का उपयोग एक चित्रण को दर्शाने के लिए करूँगा।

// for other devs who might not know keyCodes
var ESCAPE_KEY = 27;

_handleKeyDown = (event) => {
    switch( event.keyCode ) {
        case ESCAPE_KEY:
            this.state.activePopover.hide();
            break;
        default: 
            break;
    }
},

// componentWillMount deprecated in React 16.3
componentDidMount(){
    BannerDataStore.addChangeListener(this._onchange);
    document.addEventListener("click", this._handleDocumentClick, false);
    document.addEventListener("keydown", this._handleKeyDown);
},


componentWillUnmount() {
    BannerDataStore.removeChangeListener(this._onchange);
    document.removeEventListener("click", this._handleDocumentClick, false);
    document.removeEventListener("keydown", this._handleKeyDown);
},

जब से आप चीजों को करने के createClass तरीके का उपयोग कर रहे हैं, तो आपको कुछ तरीकों से बंधने की आवश्यकता नहीं है क्योंकि thisपरिभाषित प्रत्येक विधि में निहित है।

यहाँ एक प्रतिक्रियाशील घटक निर्माण की createClass विधि का उपयोग करते हुए एक कार्यशील jsfiddle है।


9
यह हर बार एक नया उदाहरण देने के लिए बाध्य होने के कारण ईवेंट श्रोता को ठीक से नहीं हटाएगा। सुनिश्चित करें कि आप परिणामों को कैश करते हैं जो दस्तावेज़ में ठीक से जोड़ने और हटाने के लिए रिटर्न को बांधते हैं
स्टीवन10172

@ Steven10172 अच्छा बिंदु, क्योंकि कंस्ट्रक्टर वास्तव में React.createClass विधि में परिभाषित नहीं है, आप हमेशा getInitialState () में बांध सकते हैं।
क्रिस सुलिवन

उपरोक्त टिप्पणियों के संबंध में, यह एक अच्छा उदाहरण है कि इवेंट श्रोताओं को ढेर करने और उपयोग करने के लिए
ढेर किया

1
ध्यान दें कि componentWillMountरिएक्ट 16.3 के रूप में पदावनत किया गया है। IMO के बजाय आपको इवेंट श्रोताओं को पंजीकृत करना चाहिए componentDidMount
इगोर अक्करमैन

24

यदि आप रिएक्ट हुक का उपयोग कर सकते हैं, तो एक अच्छा तरीका है useEffect , इसलिए ईवेंट श्रोता को केवल एक बार सब्सक्राइब किया जाएगा और घटक के अनमाउंट होने पर ठीक से अनसब्सक्राइब किया जाएगा।

नीचे दिया गया उदाहरण https://usehooks.com/useEventListener/ से निकाला गया था :

// Hook
function useEventListener(eventName, handler, element = window){
  // Create a ref that stores handler
  const savedHandler = useRef();

  // Update ref.current value if handler changes.
  // This allows our effect below to always get latest handler ...
  // ... without us needing to pass it in effect deps array ...
  // ... and potentially cause effect to re-run every render.
  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(
    () => {
      // Make sure element supports addEventListener
      // On 
      const isSupported = element && element.addEventListener;
      if (!isSupported) return;

      // Create event listener that calls handler function stored in ref
      const eventListener = event => savedHandler.current(event);

      // Add event listener
      element.addEventListener(eventName, eventListener);

      // Remove event listener on cleanup
      return () => {
        element.removeEventListener(eventName, eventListener);
      };
    },
    [eventName, element] // Re-run if eventName or element changes
  );
};

आप इसे npm से भी स्थापित कर सकते हैं, उदाहरण के लिए, npm i @use-it/event-listener- यहाँ परियोजना देखें - https://github.com/donavon/use-event-listener

फिर, इसे अपने घटक में उपयोग करने के लिए आपको बस अपने कार्यात्मक घटक को इवेंट नाम और हैंडलर के अंदर कॉल करना होगा। उदाहरण के लिए, यदि आप console.logहर बार एस्केप कुंजी को दबाए रखना चाहते हैं :

import useEventListener from '@use-it/event-listener'

const ESCAPE_KEYS = ['27', 'Escape'];

const App = () => {
  function handler({ key }) {
    if (ESCAPE_KEYS.includes(String(key))) {
      console.log('Escape key pressed!');
    }
  }

  useEventListener('keydown', handler);

  return <span>hello world</span>;
}

यदि ऐप एक कार्यात्मक घटक नहीं है, तो इसका उपयोग नहीं कर सकते हैं
ashubuntu

1
इसे पोस्ट करने के लिए धन्यवाद, मुझे अपने वैश्विक कीबोर्ड हैंडलर्स में बड़े पैमाने पर मेमोरी लीक को ठीक करने में मदद की। एफडब्ल्यूआईडब्ल्यू, "श्रोताओं को बचाओ ref" प्रभाव वास्तव में महत्वपूर्ण है - अपने इवेंट हैंडलर को useEffectनिर्भरता सरणी में पास न करें जो उन्हें जोड़ता है document.body.onKeyDown!
aendrew

@ डेंड्रू: हैंडलर को रेफरी से बचाने और सिर्फ एक फंक्शन की घोषणा करने से क्या फर्क पड़ता है?
लॉन्गक्क्ड

@thelonglqd मुझे लगता है क्योंकि अन्यथा वे कई बार इवेंट हैंडलर के रूप में जुड़ जाते हैं - मुझे उस पर उद्धरण नहीं देते हैं, हालांकि वह आधे साल पहले था और मेरी स्मृति धूमिल थी !!
aendrew

2

Jt oso के उत्तर का एक संस्करण जो इस प्रश्न के लिए अधिक प्रासंगिक है। मुझे लगता है कि यह अन्य उत्तरों की तुलना में बहुत सरल है जो श्रोता को बांधने / अनबंड करने के लिए बाहरी पुस्तकालयों या एपीआई हुक का उपयोग करते हैं।

var KEY_ESCAPE = 27;
...
    function handleKeyDown(event) {
        if (event.keyCode === KEY_ESCAPE) {
            /* do your action here */
        }
    }
...
    <div onKeyDown={handleKeyDown}>
...

4
आइटम को पहले केंद्रित किया जाना चाहिए। यदि आप एक वैश्विक ईवेंट श्रोता रखना चाहते हैं तो इसे ट्रिगर नहीं किया जा सकता है क्योंकि शुरू में शरीर तत्व केंद्रित है।
n1ru4l

आप वास्तव में उपयोग कर सकते हैंif (event.key === 'Escape')
Yifan ऐ

1

मेरे पास एक div के लिए समान आवश्यकताएं थीं जो टैब-सक्षम थीं।

मेरे लिए निम्न कोड आइटम के लिए एक कॉल के अंदर था ।मैप ((आइटम) => ...

  <div
    tabindex="0"
    onClick={()=> update(item.id)}
    onKeyDown={()=> update(item.id)}
   >
      {renderItem(item)}
  </div>

यह मेरे लिए काम किया!


1

मैं ग्लोबल इवेंट श्रोताओं को रखना चाहता था और रिएक्ट पोर्टल्स का उपयोग करने के कारण अजीब व्यवहार था। दस्तावेज़ के भीतर पोर्टल मोडल घटक पर रद्द होने के बावजूद घटना को दस्तावेज़ तत्व पर ट्रिगर किया गया था।

मैं पूरे रूट ट्री को लपेटने वाले रूट ऑब्जेक्ट पर केवल ईवेंट श्रोताओं का उपयोग करने की ओर बढ़ा। यहां समस्या यह थी कि शुरू में शरीर केंद्रित है और जड़ तत्व नहीं है, इस प्रकार पेड़ के भीतर एक तत्व को केंद्रित करने पर घटनाओं को पहले निकाल दिया जाएगा।

मैं जिस समाधान के लिए गया था वह एक टैबइंडेक्स जोड़ रहा है और स्वचालित रूप से इसे एक प्रभाव हुक के साथ केंद्रित कर रहा है।

import React from "react";

export default GlobalEventContainer = ({ children, ...props }) => {
  const rootRef = React.useRef(null);
  useEffect(() => {
    if (document.activeElement === document.body && rootContainer.current) 
      rootContainer.current.focus();
    }
  });

  return <div {...props} tabIndex="0" ref={rootRef}>{children}</div>
};
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.