Em javascript antigo, eu tenho o DIV
<div class="movie" id="my_movie">
e o seguinte código javascript
var myMovie = document.getElementById('my_movie');
myMovie.addEventListener('nv-enter', function (event) {
console.log('change scope');
});
Agora que tenho um componente React, dentro deste componente, no método render, estou retornando meu div. Como posso adicionar um ouvinte de evento para meu evento personalizado? (Estou usando esta biblioteca para aplicativos de TV - navegação )
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;
Atualização # 1:
Eu apliquei todas as idéias fornecidas nas respostas. Eu defini a biblioteca de navegação para o modo de depuração e sou capaz de navegar em meus itens de menu apenas com base no teclado (como você pode ver na captura de tela, fui capaz de navegar para Filmes 4), mas quando foco um item no menu ou pressione Enter, não vejo nada no 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;
Deixei algumas linhas comentadas porque em ambos os casos não consigo fazer com que as linhas do console sejam registradas.
Atualização # 2: esta biblioteca de navegação não funciona bem com o React com suas tags Html originais, então eu tive que definir as opções e renomear as tags para usar aria- * para não impactar o 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');
this.handleNVEnter = this.handleNVEnter.bind(this)
) e usar inicializadores de propriedade ES7 com funções de seta (handleNVEnter = enter => {}
) porque as funções de seta grande são sempre vinculadas. Se você pode usar a sintaxe ES7, siga em frente.aria-*
paradata-*
porque os atributos ARIA são de um conjunto padrão, você não pode criar o seu próprio. Os atributos de dados podem ser definidos de forma mais arbitrária para o que você quiser.Respostas:
Se você precisar lidar com eventos DOM ainda não fornecidos pelo React , deverá adicionar ouvintes DOM depois que o componente for montado:
Atualização: entre o React 13, 14 e 15, foram feitas alterações na API que afetam minha resposta. Abaixo está a forma mais recente de usar React 15 e ES7. Veja o histórico de respostas para versões 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> ); } }
Exemplo no Codepen.io
fonte
findDOMNode
. No seu casovar elem = this.refs.nv;
é o suficiente.edited Aug 19 at 6:19
texto abaixo da postagem, que o levará ao histórico de revisão .nv.dispatchEvent()
se precisar.Você poderia usar componentDidMount e componentWillUnmount métodos:
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;
fonte
Em primeiro lugar, os eventos personalizados não funcionam bem com os componentes React nativamente. Então você não pode apenas dizer
<div onMyCustomEvent={something}>
na função de renderização e tem que pensar sobre o problema.Em segundo lugar, depois de dar uma olhada na documentação da biblioteca que você está usando, o evento é realmente disparado
document.body
, portanto, mesmo que funcione, seu manipulador de eventos nunca será disparado.Em vez disso, em
componentDidMount
algum lugar em seu aplicativo, você pode ouvir nv-enter adicionandodocument.body.addEventListener('nv-enter', function (event) { // logic });
Em seguida, dentro da função de retorno de chamada, acesse uma função que altere o estado do componente ou o que você quiser fazer.
fonte