web-dev-qa-db-fra.com

Pourquoi dois-je .bind (this) pour les méthodes définies dans React classe de composants, mais pas dans la classe ES6 régulière

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.

26
pizza-r0b

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.

12
Phi Nguyen

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.

13
Felix Kling

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
8
Lyubomir

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.
0
cwingrav