reactjs - example - jsx element div has no corresponding closing tag
ReactJS-Agregar escucha de eventos personalizada al componente (3)
En javascript antiguo simple tengo el DIV
<div class="movie" id="my_movie">
y el siguiente código javascript
var myMovie = document.getElementById(''my_movie'');
myMovie.addEventListener(''nv-enter'', function (event) {
console.log(''change scope'');
});
Ahora tengo un componente React, dentro de este componente, en el método de renderizado, estoy devolviendo mi div. ¿Cómo puedo agregar un detector de eventos para mi evento personalizado? (Estoy usando esta biblioteca para aplicaciones de TV: 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;
Actualización n. ° 1:
Apliqué todas las ideas proporcionadas en las respuestas. Configuré la biblioteca de navegación en modo de depuración y puedo navegar en mis elementos de menú solo con el teclado (como puede ver en la captura de pantalla pude navegar a Películas 4) pero cuando enfoco un elemento en el menú o presione enter, no veo nada en la consola.
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;
Dejé algunas líneas comentadas porque en ambos casos no puedo hacer que se registren las líneas de la consola.
Actualización n. ° 2: esta biblioteca de navegación no funciona bien con React con sus etiquetas Html originales, por lo que tuve que configurar las opciones y cambiar el nombre de las etiquetas para usar aria- * para que no afectara a 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'');
En primer lugar, los eventos personalizados no funcionan bien con los componentes React de forma nativa.
Por lo tanto, no puede simplemente decir
<div onMyCustomEvent={something}>
en la función de representación, y tiene que pensar en el problema.
En segundo lugar, después de echar un vistazo a la documentación de la biblioteca que está utilizando, el evento se activa en
document.body
, por lo que incluso si funcionó, su controlador de eventos nunca se activará.
En cambio, dentro de
componentDidMount
en algún lugar de su aplicación, puede escuchar nv-enter agregando
document.body.addEventListener(''nv-enter'', function (event) {
// logic
});
Luego, dentro de la función de devolución de llamada, presione una función que cambie el estado del componente, o lo que sea que desee hacer.
Puede usar los métodos componentDidMount y 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;
Si necesita manejar eventos DOM no proporcionados por React , debe agregar escuchas DOM después de montar el componente:
Actualización: entre React 13, 14 y 15 se realizaron cambios en la API que afectan mi respuesta. A continuación se muestra la última forma de usar React 15 y ES7. Consulte el historial de respuestas para versiones anteriores.
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>
);
}
}