ReactJS - Bileşene özel olay dinleyicisi ekle


89

Sade eski javascript'te DIV var

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

ve aşağıdaki javascript kodu

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

Şimdi bir React Bileşenim var, bu bileşenin içinde, oluşturma yönteminde, div'ımı döndürüyorum. Özel etkinliğim için nasıl olay dinleyicisi ekleyebilirim? (Bu kitaplığı TV uygulamaları için kullanıyorum - navigasyon )

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 numaralı güncelleme:

görüntü açıklamasını buraya girin

Cevaplarda verilen tüm fikirleri uyguladım. Gezinme kitaplığını hata ayıklama moduna ayarladım ve menü öğelerimde yalnızca klavyeye dayalı olarak gezinebiliyorum (ekran görüntüsünde görebileceğiniz gibi Filmler 4'e gidebildim) ancak menüdeki bir öğeye odaklandığımda veya enter tuşuna basın, konsolda hiçbir şey görmüyorum.

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;

Bazı satırları yorum bıraktım çünkü her iki durumda da konsol hatlarının günlüğe kaydedilmesini sağlayamıyorum.

Güncelleme # 2: Bu gezinme kitaplığı, orijinal Html Etiketleri ile React ile iyi çalışmıyor, bu yüzden seçenekleri ayarlamam ve etiketleri aria- * kullanacak şekilde yeniden adlandırmam gerekti, böylece React'i etkilemeyecek.

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

@Bu dosyadaki örneği temelde kullanıyorum ( github.com/ahiipsa/navigation/blob/master/demo/index.html )
Thiago

Hem this.handleNVEnter = this.handleNVEnter.bind(this)yapıcınızda ( ) önceden bağlama yapmanıza hem de ok işlevleriyle ( handleNVEnter = enter => {}) ES7 özellik başlatıcılarını kullanmanıza gerek yoktur, çünkü fat arrow işlevleri her zaman bağlıdır. ES7 sözdizimini kullanabiliyorsanız, bununla devam edin.
Aaron Beall

1
Teşekkür ederim Aaron. Sorunu çözebildim. Şimdi çözümünüzü kullandığım için cevabınızı kabul edeceğim, ancak başka bir şey de yapmam gerekiyordu. Nagivation kitaplığı HTML etiketleri React ile iyi oynamadığından, lib yapılandırmasındaki etiket adlarını aria- * önekini kullanacak şekilde ayarlamak zorunda kaldım, sorun, olayların aynı önek kullanılarak tetiklenmesidir, bu nedenle olayı arya olarak ayarlamak -nv-enter hile yaptı! Şimdi iyi çalışıyor. Teşekkür ederim!
Thiago

Ben değişen öneriyoruz aria-*için data-*ARIA nitelikleri standart kümesinden çünkü, kendi makyaj olamaz. Veri öznitelikleri keyfi olarak istediğiniz şekilde ayarlanabilir.
Marcy Sutton

Yanıtlar:


88

Henüz React tarafından sağlanmayan DOM olaylarını işlemeniz gerekiyorsa , bileşen bağlandıktan sonra DOM dinleyicileri eklemeniz gerekir:

Güncelleme: Yanıtımı etkileyen API'de React 13, 14 ve 15 arasında değişiklikler yapıldı. Aşağıda React 15 ve ES7'yi kullanmanın en son yolu bulunmaktadır. Eski sürümler için yanıt geçmişine bakın .

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'da örnek


2
Kötüye kullanıyorsun findDOMNode. Senin durumunda var elem = this.refs.nv;yeterli.
Pavlo

1
@Pavlo Hm, haklısın, öyle görünüyor ki bu v.14'te değişti (benim kullandığım v.13'teki gibi React öğesi yerine DOM öğesini döndürmek için). Teşekkürler.
Aaron Beall

2
Neden "Bileşen kaldırıldığında DOM dinleyicisini kaldırmam gerekiyor"? Sızıntı yaratacakları bir kaynak var mı?
Fen1kz

1
@levininja edited Aug 19 at 6:19Sizi revizyon geçmişine götüren gönderinin altındaki metne tıklayın .
Aaron Beall

1
@ NicolasS.Xu React, geri çağrı props kullanmanız beklendiği için herhangi bir özel olay gönderme API'si sağlamaz ( bu yanıta bakın ), ancak gerekirse standart DOM'u kullanabilirsiniz nv.dispatchEvent().
Aaron Beall

20

ComponentDidMount ve componentWillUnmount yöntemlerini kullanabilirsiniz :

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;

Merhaba @vbarbarosh, soruyu daha fazla ayrıntıyla güncelledim
Thiago

4

Öncelikle, özel etkinlikler yerel olarak React bileşenleriyle iyi oynamıyor. Yani sadece <div onMyCustomEvent={something}>render fonksiyonunda söyleyemezsiniz ve problemin etrafında düşünmeniz gerekir.

İkinci olarak, kullanmakta olduğunuz kitaplığın belgelerine bir göz attıktan sonra, olay aslında tetiklenir document.body, bu nedenle işe yarasa bile, olay işleyiciniz asla tetiklenmez.

Bunun yerine, componentDidMountuygulamanızın herhangi bir yerinde, ekleyerek nv-enter dinleyebilirsiniz.

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

Ardından, geri arama işlevinin içinde, bileşenin durumunu veya ne yapmak isterseniz onu değiştiren bir işleve basın.


2
"Özel olayların yerel olarak React bileşenleriyle iyi oynamamasının" nedenini ayrıntılarıyla açıklayabilir misiniz?
codeful.element

1
@ codeful.element, bu sitede bununla ilgili bazı bilgiler var: custom-elements-everywhere.com/#react
Paul-Hebert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.