Considere primeiro como o this em métodos de protótipo definidos em uma classe se comporta em diferentes contextos de invocação:
class Animal {
constructor() {
console.log('Dentro do constructor da classe Animal:');
console.log(this);
}
falar() {
console.log('Dentro do método falar da classe Animal:');
console.log(this);
}
}
const animal1 = new Animal(); // this: Animal {}
animal1.falar(); // this: Animal {}
const funcaoFalar = animal1.falar;
funcaoFalar(); // this: undefined
Quando um método de classe é passado como referência e invocado diretamente (não como um método de uma instância), o valor de this não é mais vinculado à instância da classe. Isso ocorre porque as classes no JavaScript (incluindo as do React) são executadas em modo estrito por padrão. Em modo estrito, uma chamada de função simples como funcaoFalar() tem this definido como undefined.
No contexto de componentes React de classe, o mesmo princípio se aplica. Quando você passa um método como manipulador de evento, como onClick={this.handleClick}, ele é extraído do contexto da instância. Portanto, ao ser acionado pelo evento, o método é chamado como uma função comum e this se torna undefined.
Soluções para Vincular o this Corretamente em Componentes de Classe
1. Vinculação no constructor
Uma abordagem comum é vincular o método à instância atual dentro do constructor.
class Botao extends React.Component {
constructor(props) {
super(props);
this.aoClicar = this.aoClicar.bind(this);
}
aoClicar() {
console.log('this dentro de aoClicar é:', this);
}
render() {
return <button onClick={this.aoClicar}>Meu Botão</button>;
}
}
2. Vinculação inline na renderização
Você pode usar .bind() diretamente no JSX. No entanto, isso cria uma nova função a cada renderização, o que pode impactar o desempenho.
class Formulario extends React.Component {
enviarFormulario() {
console.log('Formulário enviado. this:', this);
}
render() {
return <button onClick={this.enviarFormulario.bind(this)}>Enviar</button>;
}
}
3. Usando sintaxe de propriedade de classe (arrow function na classe)
Esta é uma sintaxe popular que garante a vinculação correta de this, pois as arrow functions herdam o this do escopo léxico (a instância da classe, neste caso).
class CaixaDeTexto extends React.Component {
// A sintaxe de propriedade de classe (com arrow function) garante a vinculação de `this`.
valorMudou = () => {
console.log('Valor mudou. this:', this);
}
render() {
return <input onChange={this.valorMudou} />;
}
}
4. Usando arrow function no callback do evento
Você também pode envolver a chamada do método em uma arrow function diretamente no JSX.
class Listagem extends React.Component {
processarItem(item) {
console.log('Item processado:', item, 'this:', this);
}
render() {
return (
<button onClick={(evento) => this.processarItem('dados')}>
Processar Item
</button>
);
}
}
Vinculação de Eventos em Componentes Funcionais do React
Em componentes funcionais, não há instância de classe, portanto o conceito de this não se aplica da mesma forma. Você pode definir funções diretamente no escopo do componente e referenciá-las.
1. Referência direta da função (recomendado para sipmlicidade e desempenho)
Atribua a função de manipulação a uma variável e passe sua referência.
const MeuComponenteFuncional = () => {
const lidarComClique = () => {
console.log('O clique foi tratado.');
};
return <button onClick={lidarComClique}>Clique Aqui</button>;
};
2. Usando arrow function para passar parâmetros
Quando você precisa enviar arguemntos adicionais para o manipulador de eventos, pode usar uma arrow function inline. Note que isso pode ter implicações de desempenho se o componente for re-renderizado frequentemente.
const Card = () => {
const lidarComCliqueNoCard = (id) => {
console.log(`Card com ID ${id} foi clicado.`);
};
return (
<div onClick={() => lidarComCliqueNoCard(42)}>
Meu Card
</div>
);
};
3. Acessando o objeto de evento
O objeto de evento nativo do DOM (ex: onClick recebe um SyntheticEvent) é frequentemente necessário.
const FormularioSimples = () => {
const lidarComEnvio = (evento) => {
evento.preventDefault();
console.log('Formulário enviado!', evento.target);
};
// Combinando parâmetros e evento
const lidarComMudanca = (campo, evento) => {
console.log(`Campo ${campo} alterado para:`, evento.target.value);
};
return (
<form onSubmit={lidarComEnvio}>
<input
type="text"
onChange={(e) => lidarComMudanca('nome', e)}
/>
<button type="submit">Enviar</button>
</form>
);
};
4. Otimização com useCallback para funções estáveis
Para evitar recriações desnecessárias de funções em renders subsequentes, especialmente ao passar manipuladores para componentes filhos otimizados com React.memo, utilize o hook useCallback.
import { useCallback } from 'react';
const ComponentePai = () => {
// A função só é recriada se as dependências (segundo argumento) mudarem.
const lidarComAcao = useCallback((acao) => {
console.log(`Ação executada: ${acao}`);
}, []); // Array de dependências vazio: a função é criada uma vez.
return <ComponenteFilho onAcao={lidarComAcao} />;
};
// Componente filho otimizado com React.memo
const ComponenteFilho = React.memo(({ onAcao }) => {
return (
<button onClick={() => onAcao('clique')}>
Acionar Ação
</button>
);
});