¿Por qué es necesario usar bind en los event listener de los components?
Entiende la razón de usar bind en los event listener de un componente de React

Con poco que hayáis trabajado con React seguro que habréis tenido que recurrir a implementar algún event listener en alguno de vuestros componentes, como por ejemplo, a la hora de gestionar el click sobre un botón:
class AwesomeComponent extends React.Component{
constructor( props ){
super( props );
this.onClick = this.onClick.bind(this);
}onClick(event){
// ...
}render(){
return (
<button type="button"
onClick={this.onClick}>
Click
</button>
);
}
}
Un error habitual cuando se trabaja con los event listeners dentro de los componentes es no bindear la función al componente, de modo que cuando el evento se dispara obtenemos el error:
cannot read property ”...” of undefined
¿A qué se debe esto?
¿Cómo funciona this en Javascript?
En líneas generales, la forma en que la variable this
queda enlazada dentro de una función depende de la forma en que dicha función es invocada. De este modo, existe por un lado lo que se conoce como default binding, en el cual this
queda enlazado al objeto global donde se está ejecutando Javascript (o undefined
en el caso de que estemos en modo estricto) y que es la forma en la que se enlaza this
si ninguna otra regla se cumple:
function foo(){
console.log(this); // 'this' es el objeto global
}foo();
Por otra parte, también existe el implicit binding, en el cual this
queda enlazado al objeto que invocó el método:
var obj = {
name: 'Gerardo',
foo: function() {
console.log(this.name); // 'this' points to obj
}
};obj.foo(); // Gerardo
De este modo, cuando la función foo
se ejecuta, this
queda enlazado a la variable object
, de modo que lo que obtenemos en la consola es la propiedad name
del objeto.
Sin embargo, esto no significa que this
siempre vaya a apuntar a obj
dentro de la función foo
. En el siguiente ejemplo:
var name = "meeeec";
var otherFoo = obj.foo;
otherFoo(); // meeeec
El resultado obtenido es meeeec, es decir, la propiedad name
del objeto global, en vez de la del objeto obj
(o undefined
de estar ejecutando Javascript en modo estricto).
Esto también sucede cuando trabajamos con los callback de los event listeners o de cualquier otra función que los implemente. Por ejemplo, cuando usamos la función setTimeout
:
function setTimeout(callback, delay){ //wait for 'delay' milliseconds callback();
}setTimeout( obj.foo, 1000 );
Internamente Javascript está haciendo el siguiente paso:
callback = obj.foo;
De modo que, al igual que en el ejemplo anterior, cuando se ejecuta la función callback, la función foo
ha perdido su contexto y por tanto this
queda enlazado al objeto global (o undefined
).
La forma de solucionarlo es recurrir al método bind
(explicit hard binding), es decir, forzar a javascript a que la variable this
tome como valor el argumento pasado.
var name = "meeeec";
var otherFoo = obj.foo.bind(obj);
otherFoo(); // Gerardo
En este caso, la variable this
sí hace referencia al objeto obj
por lo que se obtiene el valor de su propiedad name
.
Recapitulando
Volviendo al comienzo del artículo, cuando trabajamos con los componentes de React y event listeners, se produce una situación muy similar a la que hemos comentado en el caso del default binding. Cuando pasamos la referencia de la función encargada de gestionar el evento como un callback:
<button type="button" onClick={this.onClick}>Click Me</button>
La función pierde su contexto por lo que al ejecutarse, la variable this
ya no hace referencia al objeto de clase que contiene el método sino que adopta el valor undefined
.
Un ejemplo simplificado de lo que está sucediendo es el siguiente:
class Foo {
constructor(name){
this.name = name
} aMethod() {
console.log(this.name);
}
}var foo = new Foo('Gerardo');
foo.aMethod(); // Gerardo// Si ahora, asignamos el método foo a otra variable
// y lo ejecutamos, obtenemos el error:
var myEventListener = foo.aMethod;
myEventListener(); // TypeError: this is undefined
Nota: el body de las clases en Javascript siempre se ejecuta en modo estricto, causa por la cual estamos obteniendo el error y no accediendo al objeto global.
Es por esta razón por la que invocamos el método bind
(habitualmente en el constructor de la propia función) cuando queremos evitar este fallo:
class Foo {
constructor(name){
this.name = name;
this.aMethod = this.aMethod.bind(this);
} aMethod(){
console.log(this.name);
}
}var foo = new Foo('Gerardo');
foo.aMethod(); // Gerardovar display = foo.aMethod;
display(); // Gerardo
¿Quieres recibir más artículos como este?
Suscríbete a nuestra newsletter: