76 votes

React this.state est indéfini ?

Je suis un tutoriel pour débutants de Pluralsight, lors de la soumission d'un formulaire, une valeur est transmise à addUser et j'ai besoin d'envoyer le nom de l'utilisateur à this.state.users mais j'obtiens l'erreur suivante

 App.jsx:14 Uncaught TypeError: Cannot read property 'users' of undefined

Composant

import React from 'react'
import User from 'user'
import Form from 'form'

class Component extends React.Component {
    constructor() {
        super()
        this.state = {
            users: null
        }
    }
    // This is triggered on form submit in different component
    addUser(userName) { 
        console.log(userName) // correctly gives String
        console.log(this.state) // this is undefined
        console.log(this.state.users) // this is the error
        // and so this code doesn't work
        /*this.setState({
            users: this.state.users.concat(userName)
        })*/
    }
    render() {
        return (
            <div>
            <Form addUser={this.addUser}/>
            </div>
            )
    }
}

export default Component

13 votes

addUser = (userName) => {

2 votes

@Andrew Pourquoi avez-vous besoin d'écrire de cette manière ?

2 votes

Pour la liaison automatique du contexte dans cette fonction, mais cela semble incorrect ici.

125voto

Vinit Raj Points 1025

Lorsque vous appelez {this.addUser} il est appelé, ici this est une instance de votre classe (composant), et donc il ne vous donne pas d'erreur parce que addUser existe dans votre classe scope , mais lorsque vous êtes sous addUser méthode que vous utilisez this pour mettre à jour le state qui existent en la portée de la classe (composant), mais vous êtes actuellement dans la portée de la classe (composant). addUser et il vous donne une erreur comme suit addUser Scope vous n'avez rien comme l'état, l'utilisateur etc. Pour résoudre ce problème, il faut donc lier les éléments suivants this pendant que vous appelez addUser Pour que votre méthode connaisse toujours l'instance de this .

Ainsi, la modification finale de votre code ressemblera à ceci:-

<Form addUser={this.addUser.bind(this)}/>

OU


Vous pouvez lier this dans le constructeur, parce que c'est l'endroit où vous devez initialiser les choses, car les méthodes du constructeur sont appelées en premier lorsque les composants sont rendus dans la page DOM .

Vous pouvez donc procéder de la manière suivante :-

  constructor(props) {
    super(props);
    this.state = {
        users: null
    }
    this.addUser=this.addUser.bind(this);
}

Et maintenant, vous pouvez l'appeler de la même manière qu'avant :-)

<Form addUser={this.addUser}/>

J'espère que cela fonctionnera, et j'ai été clair avec vous.

6 votes

Pourquoi les fonctions constructor et render comprennent-elles correctement "this", alors que addUser le considère comme indéfini ?

2 votes

@Loreno, les fonctions sont des objets et ont une portée propre. Avec bind(this) ou une expression de fonction (expression lambda), nous définissons la portée de la fonction comme étant la classe actuelle. J'espère que cela vous sera utile.

16voto

Filtenborg Points 157

Les approches de @Vinit Raj fonctionnent parfaitement - bien que je préfère utiliser la syntaxe de la fonction flèche comme ceci.

<Form addUser={ () => this.addUser() }/>

En utilisant une fonction anonyme comme celle-ci, vous ne devez pas

3 votes

L'argument "ceci pourrait causer des problèmes de performance" est une vieille rengaine. Faites des analyses comparatives pour le prouver au lieu de répandre du blabla technique. Comment pouvez-vous le prouver en appelant bind sur this est plus rapide que la création d'une nouvelle fonction fléchée ? Le fait est qu'il est probablement si petit qu'il ne peut littéralement pas être détecté dans les tests de référence. Tout écart éventuel entre les deux est largement compensé par l'incertitude de facteurs tels que la latence du réseau. Utilisez celle qui est la plus lisible, jusqu'à ce que il devient en fait un goulot d'étranglement mesurable dans votre code.

5voto

Think-Twice Points 5941

Si vous préférez utiliser la fonction flèche, procédez comme suit. La syntaxe de la fonction flèche est la suivante

addUser = event => { 
    const { value }  = event.target;
    console.log("value", value);
}

Pour éviter que cette fonction ne soit appelée à chaque rendu ou re-rendu, vous devez

Changer

<Form addUser={this.addUser}/>

Pour

<Form addUser={() => this.addUser()}/>

Ainsi, addUser n'est appelé que lorsque l'événement est survenu/déclenché.

2 votes

Elle ne sera pas appelée à chaque rendu, à moins que vous n'inligniez l'appel comme <Form addUser={this.addUser()}/>

4voto

Timmmm Points 9909

Je ne pense pas que les autres réponses l'expliquent très bien. Fondamentalement, le problème est que le langage Javascript this Le mot-clé est insensé (MDN le dit très généreusement). "se comporte un peu différemment en JavaScript par rapport à d'autres langages" ).

En résumé, voici ce qu'il en est this n'est pas nécessairement la classe dans laquelle la méthode est définie. Lorsque vous utilisez this.addUser dans votre <Form> élément, this est en fait le Form objet ! Je ne sais pas trop pourquoi React fait cela - je ne vois pas vraiment pourquoi quelqu'un voudrait cela, mais c'est facile à vérifier. Il suffit de mettre console.log(this) en addUser et vous constaterez qu'il s'agit d'une instance de Form .

Quoi qu'il en soit, la solution est assez simple : utiliser une fonction de flèche. Les fonctions fléchées font une copie de this à l'endroit où ils sont définis. Ainsi, si vous en mettez un dans votre fonction de rendu, comme l'ont suggéré d'autres personnes :

<Form addUser={() => this.addUser()}/>

Ensuite, lorsque render() est exécuté, this fera référence à la Component la fonction arrow en fera une copie et, lorsqu'elle sera exécutée, elle utilisera cette copie. Il s'agit en fait d'un raccourci pour ce code (et c'est ce que les gens faisaient avant les fonctions fléchées) :

    render() {
        const that = this; // Make a copy of this.
        return (
            <div>
            <Form addUser={function() { return that.addUser; }}/>
            </div>
            )
    }

De plus, comme d'autres personnes l'ont mentionné, la création d'une nouvelle fonction à chaque fois que vous appelez votre fonction de rendu peut avoir des conséquences en termes de performances. this une fois ailleurs. Je pense qu'il s'agit d'une meilleure approche que celle proposée par d'autres personnes :

class Component extends React.Component {
    constructor() {
        super()
        this.state = {
            users: null
        }
    }
    // This is triggered on form submit in different component
    addUser = (userName) => { 
        this.setState({
            users: this.state.users.concat(userName)
        })
    }
    render() {
        return (
            <div>
            <Form addUser={this.addUser}/>
            </div>
            )
    }
}

Mais c'est mon premier jour d'utilisation de React, et j'essaie normalement d'éviter d'utiliser des langages aussi aboyants que Javascript, alors prenez tout cela avec une pincée de sel !

3voto

J'ai eu le même problème, mais j'ai essayé d'accéder à this.state avant que l'option this.state = { ... } appel terminé. Je faisais quelque chose comme ça this.state = { ...this.function1() } y function1 = () => { a: this.state.b } . J'espère que cela aidera quelqu'un

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X