在普通的旧javascript中我有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组件,在render方法中,我正在返回我的div。如何为自定义事件添加事件侦听器? (我将此库用于电视应用 - https://github.com/ahiipsa/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;
更新#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:此导航库与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');
答案 0 :(得分:55)
如果需要handle DOM events not already provided by React,则必须在安装组件后添加DOM侦听器:
更新: 在React 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>
);
}
}
答案 1 :(得分:16)
您可以使用componentDidMount和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;
答案 2 :(得分:2)
我建议使用 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;
答案 3 :(得分:1)
首先,自定义事件本身不能与React组件很好地配合使用。所以你不能在渲染函数中说<div onMyCustomEvent={something}>
,并且必须考虑问题。
其次,在查看了您正在使用的库的文档之后,事件实际上是在document.body
上触发的,所以即使它确实有效,您的事件处理程序也永远不会触发。
相反,在应用程序某处的componentDidMount
内,您可以通过添加
document.body.addEventListener('nv-enter', function (event) {
// logic
});
然后,在回调函数中,点击一个改变组件状态的函数,或者你想做的任何事情。