ReactJS-向组件添加自定义事件侦听器

在普通的旧 HTML 中,我有 DIV

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

以及下面的 javascript 代码

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

现在我有了一个 React Component,在这个组件内部,呈现方法中,我返回了我的 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:

enter image description here

我应用了答案中提供的所有想法。我将导航库设置为调试模式,我只能在键盘上导航菜单项(正如你在屏幕截图中看到的,我能导航到 Movies 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: 这个导航库不能很好地使用 React 和它的原始 Html 标签,所以我必须设置选项并重命名标签以使用 aria-* ,这样就不会影响 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');
304925 次浏览

你可以使用 组件componentWillUnmount方法:

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;

首先,自定义事件本身不能很好地与 React 组件一起工作。所以你不能在渲染函数中只说 <div onMyCustomEvent={something}>,而必须考虑这个问题。

其次,在浏览了您正在使用的库的文档之后,事件实际上是在 document.body上触发的,因此即使它确实起作用了,您的事件处理程序也永远不会触发。

相反,在应用程序的 componentDidMount某处,您可以通过添加

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

然后,在回调函数内部,点击一个改变组件状态的函数,或者你想做的任何事情。

如果需要 处理 React 未提供的 DOM 事件,则必须在组件挂载后添加 DOM 侦听器:

更新: 在反应13、14和15之间,API 发生了改变,影响了我的回答。下面是使用 React 15和 ES7的最新方法。有关旧版本,请参见 answer history

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 上的示例

我建议使用 React.createRef()ref=this.elementRef来获取 DOM 元素引用,而不是 ReactDOM.findDOMNode(this)。通过这种方式,您可以获得对 DOM 元素的引用作为一个实例变量。

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


class MenuItem extends Component {


constructor(props) {
super(props);


this.elementRef = React.createRef();
}


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

componentDidMount() {
this.elementRef.addEventListener('nv-focus', this.handleNVFocus);
}


componentWillUnmount() {
this.elementRef.removeEventListener('nv-focus', this.handleNVFocus);
}


render() {
return (
<element ref={this.elementRef} />
)
}


}


export default MenuItem;

这里是一个 dannyjolie更详细的答案,不需要组件参考,但使用 document.body参考。

首先,在应用程序的某个地方,有一个组件方法,它将创建一个新的自定义事件并发送它。 例如,您的客户切换郎。 在这种情况下,可以向文档正文附加一个新事件:

setLang(newLang) {
// lang business logic here
// then throw a new custom event attached to the body :
document.body.dispatchEvent(new CustomEvent("my-set-lang", {detail: { newLang }}));
}

Once that done, you have another component that will need to listen to the lang switch event. For example, your customer is on a given product, and you will refresh the product having new lang as argument.

First add/remove event listener for your target component :

  componentDidMount() {
document.body.addEventListener('my-set-lang', this.handleLangChange.bind(this));
}


componentWillUnmount() {
document.body.removeEventListener('my-set-lang', this.handleLangChange.bind(this));
}


然后定义组件 my-set-langw处理程序

  handleLangChange(event) {
console.log("lang has changed to", event.detail.newLang);
// your business logic here .. this.setState({...});
}