Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS - Add custom event listener to component

Tags:

reactjs

In plain old HTML I have the DIV

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

and the following javascript code

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

Now I have a React Component, inside this component, in the render method, I am returning my div. How can I add an event listener for my custom event? (I am using this library for TV apps - navigation )

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; 

Update #1:

enter image description here

I applied all the ideas provided in the answers. I set the navigation library to debug mode and I am able to navigate on my menu items only based on the keyboard (as you can see in the screenshot I was able to navigate to Movies 4) but when I focus an item in the menu or press enter, I dont see anything in the console.

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; 

I left some lines commented because in both cases I am not able to get the console lines to be logged.

Update #2: This navigation library does not work well with React with its original Html Tags, so I had to set the options and rename the tags to use aria-* so it would not impact React.

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'); 
like image 958
Thiago Avatar asked Mar 23 '16 14:03

Thiago


People also ask

Can I use addEventListener in React?

When using React, you generally don't need to call addEventListener to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered. You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default.

How do I create a custom event in React?

Custom events are also known as “synthetic” events. We can create custom events using the Event constructor. We can now finally create our non-existent “ onDialogClose ” event as such: //First, we initialize our event const event = new Event('onDialogClose'); // Next, we dispatch the event.


1 Answers

If you need to handle DOM events not already provided by React you have to add DOM listeners after the component is mounted:

Update: Between React 13, 14, and 15 changes were made to the API that affect my answer. Below is the latest way using React 15 and ES7. See answer history for older versions.

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

Example on Codepen.io

like image 184
Aaron Beall Avatar answered Oct 16 '22 10:10

Aaron Beall