javascript - event - react setstate
¿Por qué tengo que.bind(this) para los métodos definidos en la clase de componente React, pero no en la clase ES6 normal (5)
Algo que me desconcierta es por qué cuando defino una clase de componente de reacción, los valores contenidos en
this
objeto no están definidos en los métodos definidos (
this
está disponible en los métodos de ciclo de vida) dentro de la clase a menos que use
.bind(this)
o defina el método usando una función de flecha, por ejemplo, en el siguiente código
this.state
no estará definido en la función
renderElements
porque no la
renderElements
con una función de flecha y no
.bind(this)
class MyComponent extends React.Component {
constructor() {
super();
this.state = { elements: 5 }
}
renderElements() {
const output = [];
// In the following this.state.elements will be undefined
// because I have not used .bind(this) on this method in the constructor
// example: this.renderElements = this.renderElements.bind(this)
for(let i = 0; i < this.state.elements; i ++){
output.push(<div key={i} />);
}
return output;
}
// .this is defined inside of the lifecycle methods and
// therefore do not need call .bind(this) on the render method.
render() {
return (
<div onClick={this.renderElements}></div>
);
}
}
Luego, en el siguiente ejemplo, no necesito usar
.bind(this)
o una función de flecha, está disponible como se esperaba en la función
speak
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + '' makes a noise.'');
}
}
class Dog extends Animal {
speak() {
console.log(this.name + '' barks.'');
}
}
var d = new Dog(''Mitzie'');
d.speak();
http://jsbin.com/cadoduxuye/edit?js,console
Para aclarar, mi pregunta es de dos partes.
Uno) por qué en el segundo ejemplo de código no necesito llamar a
.bind(this)
a la función
speak
, pero sí en el componente React para la función
renderElements
y Two) por qué los métodos de ciclo de vida (render, componentDidMount, etc.) ya tiene acceso a la clase ''
this
objeto, pero
renderElements
no.
En los documentos React dice lo siguiente
[React Component Class] Los métodos siguen la misma semántica que las clases ES6 normales, lo que significa que no vinculan esto automáticamente a la instancia.
Pero claramente lo hacen, como lo muestra el segundo ejemplo de código que publiqué.
Actualizar
Ambos enlaces en los primeros dos comentarios muestran un ejemplo de funcionamiento de las clases React que NO usan
.bind(this)
en los métodos de clase y funciona bien.
Pero aún en los documentos se dice explícitamente que debe vincular sus métodos o usar una función de flecha.
En un proyecto usando gulp y babel puedo reproducir.
¿Podría significar que los navegadores han actualizado las cosas?
Actualización 2
Mi ejemplo de código inicial tenía
this.renderElements()
llamado directamente en la función de representación.
Eso funcionaría como se esperaba sin vincular la función o definirla con una función de flecha.
El problema ocurre cuando pongo la función como un controlador
onClick
.
Actualización 3
El problema ocurre cuando pongo la función como un controlador
onClick
.
De hecho, no es un problema en absoluto.
El contexto de
this
cambia cuando se pasa al controlador onClick, así que así es como funciona JS.
Una expresión de función de flecha tiene una sintaxis más corta en comparación con las expresiones de función y enlaza léxicamente el valor this (
does not bind its own this, arguments, super, or new.target
). Las funciones de flecha son siempreanonymous
. Estas expresiones de función son más adecuadas para funciones que no son de método y no pueden usarse como constructores .
El método bind () crea una nueva función que, cuando se llama, tiene esta palabra clave establecida en el valor proporcionado, con una secuencia dada de argumentos que precede a cualquier proporcionada cuando se llama a la nueva función.
2. Especificaciones de componentes y ciclo de vida
Para ser absolutamente claro: la mayoría de los métodos de ciclo de vida no están vinculados, sino que se invocan en una instancia utilizando la notación de puntos (verdadero para
componentWillMount
,componentWillUnmount
,componentWillReceiveProps
y así sucesivamente ...), exceptocomponentDidMount
que está vinculado a la instancia ya que se pone en cola en la transacción.
El valor de
this
depende principalmente de cómo se
llama
la función.
Dado
d.speak();
,
this
se referirá a
d
porque la función se llama como un "método de objeto".
Pero en
<div>{this.renderElements}</div>
no estás llamando a la función.
Estás pasando la función a React que lo llamará de alguna manera.
Cuando se llama, React no sabe a qué objeto pertenecía la función, por lo que no puede establecer el valor correcto para
this
.
La unión resuelve eso
De hecho, creo que lo que realmente quieres es
<div>{this.renderElements()}</div>
// call function ^^
es decir, llamar a la función como un método de objeto. Entonces no tienes que atarlo.
Echa un vistazo a
MDN
para aprender más sobre
this
.
Los controladores de eventos en el componente no se vincularán automáticamente a la instancia del componente como otros métodos (métodos de ciclo de vida ...).
class MyComponent extends React.Component {
render(){
return (
<div onClick={this.renderElements}>
{this.renderElements()} <-- `this` is still in side the MyComponent context
</div>
)
}
}
//under the hood
var instance = new MyComponent();
var element = instance.render();
//click on div
element.onClick() <-- `this` inside renderElements refers to the window object now
Mira este ejemplo para entender
this
más:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + '' makes a noise.'');
}
}
class Dog extends Animal {
run(){
console.log(this.name + '' runs'');
}
speak() {
console.log(this.name + '' barks.'');
this.run(); <-- `this` is still in the Dog context
return {onRun : this.run};
}
}
var d = new Dog(''Mitzie'');
var myDog = d.speak();
myDog.onRun() <-- `this` is now in the global context which is the `window` object
Puedes consultar este article para más información.
Solo pon siempre el
autoBind(this);
codifique en su constructor y nunca se preocupe por los punteros de método.
npm install --save auto-bind-inheritance
const autoBind = require(''auto-bind-inheritance'');
class Animal {
constructor(name) {
autoBind(this);
this.name = name;
}
printName() { console.log(this.name); }
...
}
let m = new Animal(''dog'');
let mpntr = m.printName;
m.printName() //> ''dog''
mpntr() //> ''dog'', because auto-bind, binds ''this'' to the method.
Funciones en las clases de ES6
: el caso lo explica muy bien @Felix Kling.
Cada vez que llama a una función en un objeto,
this
apunta al objeto.
Métodos de ciclo de vida en React.Component
: cada vez que React
crea una
instancia de su componente como
myComponent = new MyComponent()
sabe en qué objeto invocar los métodos de ciclo de vida, es decir,
myComponent
.
Por lo tanto, una simple llamada
myComponent.componentDidUpdate()
hace que esté disponible en el método del ciclo de vida de
componentDidUpdate
.
Lo mismo para los otros métodos de ciclo de vida.
Handlers & Bound en React.Component
-
this.state
undefined
está
undefined
, porque
this
realidad es una
window
:
this.state
y vea.
La razón es que
React invoca manejadores en el contexto global, a menos que tenga el manejador vinculado a otro contexto que anule la
window
(vea también la respuesta de @Phi Nguyen).
Creo que lo han hecho para permitirle una mayor flexibilidad, porque en aplicaciones complejas su controlador puede provenir de otro componente pasado por accesorios y luego le gustaría tener la posibilidad de decir:
"Oye, reacciona,
this
no es mi componente, pero es padre ".
La documentación de React es una oferta engañosa cuando dice
Los métodos siguen la misma semántica que las clases ES6 normales, lo que significa que no vinculan esto automáticamente a la instancia.
Lo que quieren decir es que
var dog = new Dog(''Mitzie'');
speak = d.speak;
dog.speak() // this will be dog, because the function is called on dog
speak() // this will be window, and not dog, because the function is not bound