ReactJS - घटक में कस्टम इवेंट श्रोता जोड़ें


89

सादे पुरानी जावास्क्रिप्ट में मेरे पास डीआईवी है

<div class="movie" id="my_movie">

और निम्नलिखित जावास्क्रिप्ट कोड

var myMovie = document.getElementById('my_movie');
myMovie.addEventListener('nv-enter', function (event) {
     console.log('change scope');
});

अब मेरे पास एक प्रतिक्रिया घटक है, इस घटक के अंदर, रेंडर विधि में, मैं अपना div लौटा रहा हूं। मैं अपने कस्टम इवेंट के लिए इवेंट श्रोता कैसे जोड़ सकता हूं? (मैं टीवी ऐप्स के लिए इस लाइब्रेरी का उपयोग कर रहा हूं - नेविगेशन )

import React, { Component } from 'react';

class MovieItem extends Component {

  render() {

    if(this.props.index === 0) {
      return (
        <div aria-nv-el aria-nv-el-current className="menu_item nv-default">
            <div className="indicator selected"></div>
            <div className="category">
                <span className="title">{this.props.movieItem.caption.toUpperCase()}</span>
            </div>
        </div>
      );
    }
    else {
      return (
        <div aria-nv-el className="menu_item nv-default">
            <div className="indicator selected"></div>
            <div className="category">
                <span className="title">{this.props.movieItem.caption.toUpperCase()}</span>
            </div>
        </div>
      );
    }
  }

}

export default MovieItem;

अपडेट # 1:

यहाँ छवि विवरण दर्ज करें

मैंने उत्तरों में प्रदान किए गए सभी विचारों को लागू किया। मैंने नेविगेशन लाइब्रेरी को डिबग मोड में सेट किया है और मैं केवल कीबोर्ड पर आधारित अपने मेनू आइटम पर नेविगेट करने में सक्षम हूं (जैसा कि आप स्क्रीनशॉट में देख सकते हैं कि मैं मूवी 4 में नेविगेट करने में सक्षम था) लेकिन जब मैं मेनू में किसी आइटम पर ध्यान केंद्रित करता हूं या प्रेस दर्ज करें, मुझे कंसोल में कुछ भी दिखाई नहीं दे रहा है।

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class MenuItem extends Component {

  constructor(props) {
    super(props);
    // Pre-bind your event handler, or define it as a fat arrow in ES7/TS
    this.handleNVFocus = this.handleNVFocus.bind(this);
    this.handleNVEnter = this.handleNVEnter.bind(this);
    this.handleNVRight = this.handleNVRight.bind(this);
  }

  handleNVFocus = event => {
      console.log('Focused: ' + this.props.menuItem.caption.toUpperCase());
  }

  handleNVEnter = event => {
      console.log('Enter: ' + this.props.menuItem.caption.toUpperCase());
  }

  handleNVRight = event => {
      console.log('Right: ' + this.props.menuItem.caption.toUpperCase());
  }

  componentDidMount() {
    ReactDOM.findDOMNode(this).addEventListener('nv-focus', this.handleNVFocus);
    ReactDOM.findDOMNode(this).addEventListener('nv-enter', this.handleNVEnter);
    ReactDOM.findDOMNode(this).addEventListener('nv-right', this.handleNVEnter);
    //this.refs.nv.addEventListener('nv-focus', this.handleNVFocus);
    //this.refs.nv.addEventListener('nv-enter', this.handleNVEnter);
    //this.refs.nv.addEventListener('nv-right', this.handleNVEnter);
  }

  componentWillUnmount() {
    ReactDOM.findDOMNode(this).removeEventListener('nv-focus', this.handleNVFocus);
    ReactDOM.findDOMNode(this).removeEventListener('nv-enter', this.handleNVEnter);
    ReactDOM.findDOMNode(this).removeEventListener('nv-right', this.handleNVRight);
    //this.refs.nv.removeEventListener('nv-focus', this.handleNVFocus);
    //this.refs.nv.removeEventListener('nv-enter', this.handleNVEnter);
    //this.refs.nv.removeEventListener('nv-right', this.handleNVEnter);
  }

  render() {
    var attrs = this.props.index === 0 ? {"aria-nv-el-current": true} : {};
    return (
      <div ref="nv" aria-nv-el {...attrs} className="menu_item nv-default">
          <div className="indicator selected"></div>
          <div className="category">
              <span className="title">{this.props.menuItem.caption.toUpperCase()}</span>
          </div>
      </div>
    )
  }

}

export default MenuItem;

मैंने कुछ पंक्तियों को टिप्पणी के लिए छोड़ दिया क्योंकि दोनों ही मामलों में मैं कंसोल लाइनों को लॉग इन करने में सक्षम नहीं हूं।

अपडेट # 2: यह नेविगेशन लाइब्रेरी अपने मूल एचटीएमएल टैग के साथ रिएक्ट के साथ अच्छी तरह से काम नहीं करती है, इसलिए मुझे विकल्प सेट करना पड़ा और टैग का नाम बदलकर आरिया का उपयोग करना पड़ा- * तो यह रिएक्ट को प्रभावित नहीं करेगा।

navigation.setOption('prefix','aria-nv-el');
navigation.setOption('attrScope','aria-nv-scope');
navigation.setOption('attrScopeFOV','aria-nv-scope-fov');
navigation.setOption('attrScopeCurrent','aria-nv-scope-current');
navigation.setOption('attrElement','aria-nv-el');
navigation.setOption('attrElementFOV','aria-nv-el-fov');
navigation.setOption('attrElementCurrent','aria-nv-el-current');

@ मैं मूल रूप से इस फ़ाइल से उदाहरण का उपयोग कर रहा हूं ( github.com/ahiipsa/navigation/blob/master/demo/index.html )
थिएगो

आपको अपने कंस्ट्रक्टर ( this.handleNVEnter = this.handleNVEnter.bind(this)) में पूर्व-बाइंड दोनों की आवश्यकता नहीं है और तीर फ़ंक्शन ( handleNVEnter = enter => {}) के साथ ES7 प्रॉपर्टी इनिशियलाइज़र का उपयोग करें क्योंकि वसा एरो फ़ंक्शंस हमेशा बाध्य होते हैं। यदि आप ES7 सिंटैक्स का उपयोग कर सकते हैं तो बस उसी के साथ जाएं।
आरोन बेयेल

1
धन्यवाद हारून। मैं समस्या को ठीक करने में सक्षम था। मैं आपके उत्तर को स्वीकार करने जा रहा हूं क्योंकि मैं अब आपके समाधान का उपयोग कर रहा हूं लेकिन मुझे कुछ और भी करना था। चूंकि नगेटेशन लाइब्रेरी एचटीएमएल टैग रिएक्ट के साथ अच्छा नहीं खेलता है, मुझे एरिया- * उपसर्ग का उपयोग करने के लिए लिबर कॉन्फ़िगरेशन में टैग नाम सेट करना था, समस्या यह है कि घटनाओं को भी एक ही उपसर्ग का उपयोग करके ट्रिगर किया गया था, इसलिए एरिया के लिए ईवेंट सेट करना -nv- दर्ज किया चाल! अब इसका काम ठीक है। धन्यवाद!
थियागो

मैं बदलने की सलाह देते हैं aria-*करने के लिए data-*क्योंकि ARIA गुण एक मानक सेट से कर रहे हैं, आप अपने खुद के लिए नहीं कर सकते हैं। डेटा विशेषताएँ मनमाने ढंग से सेट की जा सकती हैं जो आप चाहते हैं।
मैरिएन सटन २ '

जवाबों:


88

यदि आपको DOM द्वारा पहले से उपलब्ध कराई गई DOM घटनाओं को संभालने की आवश्यकता है , तो घटक के माउंट होने के बाद आपको DOM श्रोताओं को जोड़ना होगा:

अद्यतन: प्रतिक्रिया 13, 14, और 15 के बीच एपीआई में परिवर्तन किए गए जो मेरे उत्तर को प्रभावित करते हैं। नीचे रिएक्ट 15 और ईएस 7 का उपयोग करने का नवीनतम तरीका है। पुराने संस्करणों के लिए उत्तर इतिहास देखें ।

class MovieItem extends React.Component {

  componentDidMount() {
    // When the component is mounted, add your DOM listener to the "nv" elem.
    // (The "nv" elem is assigned in the render function.)
    this.nv.addEventListener("nv-enter", this.handleNvEnter);
  }

  componentWillUnmount() {
    // Make sure to remove the DOM listener when the component is unmounted.
    this.nv.removeEventListener("nv-enter", this.handleNvEnter);
  }

  // Use a class arrow function (ES7) for the handler. In ES6 you could bind()
  // a handler in the constructor.
  handleNvEnter = (event) => {
    console.log("Nv Enter:", event);
  }

  render() {
    // Here we render a single <div> and toggle the "aria-nv-el-current" attribute
    // using the attribute spread operator. This way only a single <div>
    // is ever mounted and we don't have to worry about adding/removing
    // a DOM listener every time the current index changes. The attrs 
    // are "spread" onto the <div> in the render function: {...attrs}
    const attrs = this.props.index === 0 ? {"aria-nv-el-current": true} : {};

    // Finally, render the div using a "ref" callback which assigns the mounted 
    // elem to a class property "nv" used to add the DOM listener to.
    return (
      <div ref={elem => this.nv = elem} aria-nv-el {...attrs} className="menu_item nv-default">
        ...
      </div>
    );
  }

}

Codepen.io पर उदाहरण


2
आप दुरुपयोग कर रहे हैं findDOMNode। आपके मामले var elem = this.refs.nv;में पर्याप्त है।
पावलो

1
@ पाब्लो एचएम, आप सही कह रहे हैं, ऐसा लगता है कि यह v.14 में बदल गया (प्रतिक्रिया तत्व के बजाय DOM तत्व को वापस करने के लिए v.13 में, जो कि मैं उपयोग कर रहा हूं)। धन्यवाद।
एरॉन बेयेल

2
जब घटक अनमाउंट हो, तो मुझे DOM श्रोता को निकालना सुनिश्चित करने की आवश्यकता क्यों है? क्या कोई स्रोत है कि वे एक रिसाव पैदा करेंगे?
फेन

1
@levininja edited Aug 19 at 6:19पोस्ट के नीचे दिए गए पाठ पर क्लिक करें , जो आपको इतिहास को संशोधित करने के लिए ले जाता है ।
हारून बाइलल

1
@ NicolasS.Xu प्रतिक्रिया किसी भी कस्टम ईवेंट प्रेषण एपीआई प्रदान नहीं करता है क्योंकि आप कॉलबैक प्रॉप्स ( इस उत्तर को देखें ) का उपयोग करने की उम्मीद कर रहे हैं , लेकिन nv.dispatchEvent()यदि आपको ज़रूरत है तो आप मानक डोम का उपयोग कर सकते हैं ।
आरोन बेयेल

20

आप घटक का उपयोग कर सकते हैंमाउंट और कंपोनेंटव्यूनल विधि:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class MovieItem extends Component
{
    _handleNVEvent = event => {
        ...
    };

    componentDidMount() {
        ReactDOM.findDOMNode(this).addEventListener('nv-event', this._handleNVEvent);
    }

    componentWillUnmount() {
        ReactDOM.findDOMNode(this).removeEventListener('nv-event', this._handleNVEvent);
    }

    [...]

}

export default MovieItem;

हाय @vbarbarosh, मैंने अधिक विवरण के साथ प्रश्न को अद्यतन किया
थियागो

4

सबसे पहले, कस्टम इवेंट्स मूल रूप से रिएक्ट घटकों के साथ अच्छा नहीं खेलते हैं। तो आप सिर्फ <div onMyCustomEvent={something}>रेंडर फ़ंक्शन में नहीं कह सकते हैं , और समस्या के बारे में सोचना होगा।

दूसरे, आपके द्वारा उपयोग किए जा रहे पुस्तकालय के लिए प्रलेखन पर एक नज़र डालने के बाद, इस घटना को वास्तव में निकाल दिया जाता है document.body, इसलिए यदि यह काम करता है , तो भी आपका ईवेंट हैंडलर कभी भी ट्रिगर नहीं होगा।

इसके बजाय, componentDidMountआपके आवेदन में कहीं अंदर , आप जोड़कर nv-enter सुन सकते हैं

document.body.addEventListener('nv-enter', function (event) {
    // logic
});

फिर, कॉलबैक फ़ंक्शन के अंदर, एक फ़ंक्शन को हिट करें जो घटक की स्थिति को बदलता है, या आप जो भी करना चाहते हैं।


2
क्या आप इस कारण के बारे में विस्तार से बताने में मदद कर सकते हैं कि "कस्टम इवेंट्स मूल रूप से रिएक्ट घटकों के साथ अच्छा क्यों नहीं खेलते हैं"?
codeful.element 20

1
@ codeful.element, इस साइट के बारे में कुछ जानकारी है: custom-elements-everywhere.com/#react
Paul-Hebert
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.