Manipulando Eventos

Manipular eventos em elementos React é muito semelhante à manipular eventos em elementos do DOM. Existem algumas diferenças sintáticas:

  • Eventos em React são nomeados usando camelCase ao invés de letras minúsculas.
  • Com o JSX tu passas uma função como manipulador de eventos ao invés de um texto.

Por exemplo, com HTML:

<button onclick="activateLasers()">
  Ativar Lasers
</button>

é ligeiramente diferente em React:

<button onClick={activateLasers}>
  Ativar Lasers
</button>

Outra diferença é que não podes retornar false para evitar o comportamento padrão no React. Deves chamar preventDefault explícitamente. Por exemplo, com HTML simples, para evitar que um link abra uma nova página, podes escrever:

<a href="#" onclick="console.log('O link foi clicado.'); return false">
  Clica-me
</a>

Em React, isto poderia ser:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('O link foi clicado.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Clica-me
    </a>
  );
}

Aqui, ”e” é um synthetic event. O React define esses eventos sintéticos de acordo com a especificação W3C. Então, não precisamos nos preocupar com a compatibilidade entre navegadores. Vê a página SyntheticEvent para saberes mais.

Ao usar o React geralmente não precisas chamar addEventListener para adicionar listeneres à um elemento no DOM depois que ele é criado. Ao invés disso podes apenas definir um listener quando o elemento é inicialmente renderizado.

Quando defines um componente usando uma classe do ES6, um padrão comum é que um manipulador de eventos seja um método na classe. Por exemplo, este componente Toggle renderiza um botão que permite ao utilizador alternar entre os estados “ON” e “OFF”:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // Aqui utilizamos o `bind` para que o `this` funcione dentro do nosso callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Experimenta no CodePen

Precisas ter cuidado com o significado do this nos callbacks do JSX. Em JavaScript, os métodos de classe não são vinculados por padrão. Se esqueceres de fazer o bind de this.handleClick e passá-lo para um onClick, o this será undefined quando a função for realmente chamada.

Este não é um comportamento específico do React. É como funcionam as funções em JavaScript. Geralmente, se referires à um método sem () depois dele, como onClick={this.handleClick}, deves fazer o bind desse método.

Se chamar bind incomoda-te, há duas maneiras de contornar isto. Se estiveres a usar a sintaxe experimental de campos de classe pública, podes usar campos de classe para vincular callbacks corretamente:

class LoggingButton extends React.Component {
  // Esta sintaxe garante que o `this` seja vinculado ao handleClick.
  // Atenção: esta é uma sintaxe *experimental*.
  handleClick = () => {
    console.log('this é:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Clica-me
      </button>
    );
  }
}

Esta sintaxe é habilitada por padrão em Create React App.

Se não estiveres a usar a sintaxe de campos de classe, poderás usar uma função arrow como callback:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this é:', this);
  }

  render() {
    // Esta sintaxe garante que o `this` seja vinculado ao handleClick.
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Clica-me
      </button>
    );
  }
}

O problema com esta sintaxe é que um callback diferente é criado toda vez que o LoggingButton é renderizado. Na maioria dos casos não há problema. No entanto, se este callback for passado para componentes inferiores através de props, estes componentes poderão fazer uma renderização adicional. Geralmente recomendamos a vinculação no construtor ou a sintaxe dos campos de classe para evitar este tipo de problema de desempenho.

Passar argumentos para manipuladores de eventos

Dentro de uma estrutura de repetição é comum desejar passar um parâmetro extra para um manipulador de evento. Por exemplo, se id é o ID de identificação da linha, qualquer um dos dois a seguir funcionará:

<button onClick={(e) => this.deleteRow(id, e)}>Apagar Linha</button>
<button onClick={this.deleteRow.bind(this, id)}>Apagar Linha</button>

As duas linhas acima são equivalentes e usam função arrow e Function.prototype.bind respectivamente.

Em ambos os casos, o argumento e que representa o evento de React será passado como segundo argumento após o ID. Com uma função arrow, nós temos que passá-lo explicitamente. Mas com o bind outros argumentos adicionais serão automaticamente encaminhados.