Quelque chose me laisse perplexe, c'est pourquoi lorsque je définis une classe de composants React, les valeurs contenues dans l'objet this
ne sont pas définies dans les méthodes définies (this
est disponible dans les méthodes de cycle de vie) dans la classe, sauf si j'utilise .bind(this)
ou définissez la méthode à l'aide d'une fonction flèche par exemple dans le code suivant this.state
sera indéfini dans la fonction renderElements
car je ne l'ai pas défini avec une fonction flèche et n'a pas utilisé .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>
);
}
}
Dans l'exemple suivant, je n'ai pas besoin d'utiliser .bind(this)
ou une fonction flèche, this
est disponible comme prévu dans la fonction 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
Pour clarifier, ma question est en deux parties. Un) pourquoi dans le deuxième exemple de code, je n'ai pas besoin d'appeler .bind(this)
à la fonction speak
, mais je le fais dans le composant React pour le composant renderElements
et deux) pourquoi les méthodes de cycle de vie (render, componentDidMount, etc.) ont-elles déjà accès à l'objet de classe 'this
, mais pas à renderElements
.
Dans les documents React il dit ce qui suit
[React Component Class] Les méthodes suivent la même sémantique que les classes ES6 normales, ce qui signifie qu'elles ne le lient pas automatiquement à l'instance.
Mais c'est clairement le cas, comme le montre le deuxième exemple de code que j'ai publié.
Mise à jour
Les deux liens dans les deux premiers commentaires montrent un exemple de travail de React classes NE PAS utiliser .bind(this)
sur les méthodes de classe et cela fonctionne très bien. Mais toujours dans la documentation, il est explicitement dit que vous avez besoin pour lier vos méthodes, ou utiliser une fonction flèche. Dans un projet utilisant gulp et babel je peux reproduire. Cela pourrait-il signifier que les navigateurs ont mis à jour les choses?
Mise à jour 2
Mon exemple de code initial avait this.renderElements()
appelé directement dans la fonction de rendu. Cela fonctionnerait comme prévu sans lier la fonction ou la définir avec une fonction de flèche. Le problème se produit lorsque je mets la fonction en tant que gestionnaire onClick
.
Mise à jour 3
Le problème se produit lorsque je mets la fonction en tant que gestionnaire
onClick
.
En fait, ce n'est pas du tout un problème. Le contexte de this
change lorsqu'il est transmis au gestionnaire onClick, c'est donc ainsi que fonctionne JS.
Les gestionnaires d'événements du composant ne seront pas liés automatiquement à l'instance du composant comme les autres méthodes (méthodes du cycle de vie ...).
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
Consultez cet exemple pour comprendre this
plus:
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
Vous pouvez vérifier cela article pour plus d'informations.
La valeur de this
dépend principalement de la façon dont la fonction est appelée . Étant donné d.speak();
, this
fera référence à d
car la fonction est appelée comme une "méthode objet".
Mais en <div>{this.renderElements}</div>
vous n'appelez pas la fonction. Vous passez la fonction à React qui l'appellera d'une manière ou d'une autre. Quand elle est appelée, React ne sait pas à quel objet la fonction "appartenait") donc il ne peut pas définir la bonne valeur pour this
. La liaison résout ce
Je pense que ce que tu veux vraiment c'est
<div>{this.renderElements()}</div>
// call function ^^
c'est-à-dire appeler la fonction comme méthode d'objet. Ensuite, vous n'avez pas à le lier.
Jetez un œil à MDN pour en savoir plus sur this
.
Fonctions dans les classes ES6 - le cas est très bien expliqué par @Felix Kling. Chaque fois que vous appelez une fonction sur un objet, this
pointe vers l'objet.
Méthodes de cycle de vie dans React.Component - chaque fois que React instancie votre composant comme myComponent = new MyComponent()
il sait sur quel objet appeler les méthodes de cycle de vie, à savoir myComponent . Un simple appel myComponent.componentDidUpdate()
rend this
disponible dans la méthode de cycle de vie componentDidUpdate
. Idem pour les autres méthodes de cycle de vie.
Handlers & Bound in React.Component - this.state
est undefined
, parce que this
est en fait window
- enregistrez-le et voyez. La raison en est que React appelle des gestionnaires sur le contexte global, à moins que le gestionnaire ne soit lié à un autre contexte qui remplace window
(voir @ Réponse de Phi Nguyen également). Je pense qu'ils l'ont fait pour vous permettre plus de flexibilité, car dans des applications complexes, votre gestionnaire peut provenir d'un autre composant passé par des accessoires et vous voudriez alors avoir la possibilité de dire: "Hey, React - this
n'est pas mon composant, mais son parent. "
La documentation de React est une offre trompeuse lorsqu'elle dit
Les méthodes suivent la même sémantique que les classes ES6 normales, ce qui signifie qu'elles ne le lient pas automatiquement à l'instance.
Ce qu'ils veulent dire, c'est 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
Mettez toujours le code autoBind(this);
dans votre constructeur et ne vous souciez jamais des pointeurs de méthode.
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.