React - TypeError non capturé: impossible de lire la propriété 'setState' de undefined

316

Je reçois l'erreur suivante

TypeError non capturé: impossible de lire la propriété «setState» de non défini

même après liaison delta dans le constructeur.

class Counter extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            count : 1
        };

        this.delta.bind(this);
    }

    delta() {
        this.setState({
            count : this.state.count++
        });
    }

    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick={this.delta}>+</button>
            </div>
        );
    }
}
Dangling_pointer
la source
4
dans ES6, vous pouvez utiliser la fonction flèche pour la déclaration de fonction pour résoudre ce problème.
Tal
^ cela devrait être la bonne réponse
Jordec
J'ai changé ma fonction de réponse en ES6, et hurrey, son fonctionnement.
Ashwani Garg

Réponses:

449

Cela est dû au fait de this.deltane pas y être lié this.

Afin de lier l'ensemble this.delta = this.delta.bind(this)dans le constructeur:

constructor(props) {
    super(props);

    this.state = {
        count : 1
    };

    this.delta = this.delta.bind(this);
}

Actuellement, vous appelez bind. Mais bind renvoie une fonction liée. Vous devez définir la fonction sur sa valeur liée.

Davin Tryon
la source
188
À quoi bon les classes ES6 si leurs méthodes n'ont pas de thisliaison lexicale appropriée , puis n'exposent même pas une syntaxe pour lier leur contexte directement sur leur définition!?
AgmLauncher
1
je comprends votre point mais si j'écris du code dans componentWillMount () alors comment vais-je lier
suresh pareek
1
@sureshpareek Une fois que vous liez votre fonction dans le constructeur, elle doit ensuite être liée lorsque vous l'appelez à partir de n'importe quel hook de cycle de vie.
Levi Fuller
4
Venant du monde Android / Java, je suis déconcerté
Tudor
3
@AgmLauncher l'utilisation des fonctions Lambda lie implicitement cela. Si vous définissez deltacomme delta = () => { return this.setState({ count: this.state.count++ }); };le code fonctionnerait également. Expliqué ici: hackernoon.com/…
K. Rhoda
145

Dans ES7 + (ES2016), vous pouvez utiliser l' opérateur de syntaxe de liaison de la fonction expérimentale ::pour effectuer la liaison. C'est un sucre syntaxique et fera la même chose que la réponse de Davin Tryon.

Vous pouvez ensuite réécrire this.delta = this.delta.bind(this);dansthis.delta = ::this.delta;


Pour ES6 + (ES2015), vous pouvez également utiliser la fonction de flèche ES6 + ( =>) pour pouvoir l'utiliser this.

delta = () => {
    this.setState({
        count : this.state.count + 1
    });
}

Pourquoi ? Du document Mozilla:

Jusqu'à ce que les fonctions de direction, chaque nouvelle fonction définie par son propre cette valeur [...]. Cela s'est avéré ennuyeux avec un style de programmation orienté objet.

Les fonctions fléchées capturent cette valeur du contexte englobant [...]

Fabien Sa
la source
3
Bel article décrivant cela en détail: reactkungfu.com/2015/07/…
Edo
Quel est l'avantage d'utiliser l'un sur l'autre, outre la syntaxe?
Jeremy D
2
La syntaxe de liaison est plus propre car vous pouvez conserver la portée normale de votre méthode.
Fabien Sa
La syntaxe de liaison ne fait pas partie d'ES2016 ou ES2017.
Felix Kling
2
@stackoverflow devrait ajouter la possibilité d'ajouter une prime à n'importe quelle réponse.
Gabe
29

Il existe une différence de contexte entre les classes ES5 et ES6. Il y aura donc également une petite différence entre les implémentations.

Voici la version ES5:

var Counter = React.createClass({
    getInitialState: function() { return { count : 1 }; },
    delta: function() {
        this.setState({
            count : this.state.count++
        });
    },
    render: function() {
        return (
            <div>
              <h1>{this.state.count}</h1>
              <button onClick={this.delta}>+</button>
            </div>
            );
    }
});

et voici la version ES6:

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = { count : 1 };
    }

    delta() {
        this.setState({
            count : this.state.count++
        });
    }

    render() {
        return (
            <div>
              <h1>{this.state.count}</h1>
              <button onClick={this.delta.bind(this)}>+</button>
            </div>
            );
    }
}

Faites juste attention, à côté de la différence de syntaxe dans l'implémentation de classe, il y a une différence dans la liaison du gestionnaire d'événements.

Dans la version ES5, c'est

              <button onClick={this.delta}>+</button>

Dans la version ES6, c'est:

              <button onClick={this.delta.bind(this)}>+</button>
Tao Wang
la source
L'utilisation des fonctions fléchées ou de la liaison dans JSX est une mauvaise pratique. stackoverflow.com/questions/36677733/… .
Fabien Sa
24

Lorsque vous utilisez du code ES6 dans React, utilisez toujours les fonctions fléchées, car ce contexte est automatiquement lié à celui-ci

Utilisez ceci:

(videos) => {
    this.setState({ videos: videos });
    console.log(this.state.videos);
};

au lieu de:

function(videos) {
    this.setState({ videos: videos });
    console.log(this.state.videos);
};
Ignace Andrew
la source
2
Si vous utilisez la fonction flèche et la variable de paramètre est la même que la variable clé , je recommanderais de l'utiliser comme this.setState({videos});
jayeshkv
C'est ce qui m'a fait ça. Je suis nouveau sur node, et les documents du module axios étaient incompatibles avec react et setState
dabobert
20

Vous n'avez rien à lier, utilisez simplement les fonctions fléchées comme ceci:

class Counter extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            count: 1
        };

    }
    //ARROW FUNCTION
    delta = () => {
        this.setState({
            count: this.state.count++
        });
    }

    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick={this.delta}>+</button>
            </div>
        );
    }
}
Gabo Ruiz
la source
cela fonctionne quelle est la différence s'il vous plaît pourquoi est-ce?
Ridha Rezzag
4
La portée this avec les fonctions fléchées est héritée du contexte. Avec les fonctions régulières, cela fait toujours référence à la fonction la plus proche, tandis qu'avec les fonctions fléchées, ce problème est supprimé, et vous n'aurez plus jamais besoin d'écrire var that = this. @RezzagRidha
Gabo Ruiz
1
À partir de 2019, c'est la voie à suivre (Y)
MH
6

Vous pouvez aussi utiliser:

<button onClick={()=>this.delta()}>+</button>

Ou:

<button onClick={event=>this.delta(event)}>+</button>

Si vous passez quelques paramètres ..

Jaroslav Benc
la source
C'est une mauvaise pratique d'utiliser les fonctions fléchées dans JSX
Gabe
5

Vous devez le lier au constructeur et n'oubliez pas que les modifications apportées au constructeur doivent redémarrer le serveur. Sinon, vous finirez avec la même erreur.

Anil
la source
1
Tirait mes cheveux parce que je n'ai pas redémarré le serveur.
kurtcorbett
5

Vous devez lier vos méthodes avec 'this' (objet par défaut). Donc, quelle que soit votre fonction, il suffit de la lier dans le constructeur.

constructor(props) {
    super(props);
    this.state = { checked:false };

    this.handleChecked = this.handleChecked.bind(this);
}

handleChecked(){
    this.setState({
        checked: !(this.state.checked)
    })
}

render(){
    var msg;

    if(this.state.checked){
        msg = 'checked'
    }
    else{
        msg = 'not checked'
    }

    return (
        <div>               
            <input type='checkbox' defaultChecked = {this.state.checked} onChange = {this.handleChecked} />
            <h3>This is {msg}</h3>
        </div>
    );
Shahrukh Anwar
la source
4

Cette erreur peut être résolue par différentes méthodes -

  • Si vous utilisez la syntaxe ES5 , selon la documentation React js, vous devez utiliser la méthode bind .

    Quelque chose comme ça pour l'exemple ci-dessus:

    this.delta = this.delta.bind(this)

  • Si vous utilisez la syntaxe ES6 , vous n'avez pas besoin d'utiliser la méthode bind , vous pouvez le faire avec quelque chose comme ceci:

    delta=()=>{ this.setState({ count : this.state.count++ }); }

hardik chugh
la source
2

Il existe deux solutions à ce problème:

La première solution consiste à ajouter un constructeur à votre composant et à lier votre fonction comme ci-dessous:

constructor(props) {
        super(props);

        ...

        this.delta = this.delta.bind(this);
    }

Faites donc ceci:

this.delta = this.delta.bind(this); 

Au lieu de cela:

this.delta.bind(this);

La deuxième solution consiste à utiliser une fonction flèche à la place:

delta = () => {
       this.setState({
           count : this.state.count++
      });
   }

En fait, la fonction de flèche ne lie pas la sienne this. Fonctions flèches lexicalement bindleur contexte thisse réfère donc en fait au contexte d'origine .

Pour plus d'informations sur la fonction de liaison:

Fonction Bind Comprendre JavaScript Bind ()

Pour plus d'informations sur la fonction flèche:

Javascript ES6 - Fonctions fléchées et lexical this

Mselmi Ali
la source
1

vous devez lier un nouvel événement avec ce mot - clé comme je le mentionne ci-dessous ...

class Counter extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            count : 1
        };

        this.delta = this.delta.bind(this);
    }

    delta() {
        this.setState({
            count : this.state.count++
        });
    }

    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick={this.delta}>+</button>
            </div>
        );
      }
    }
Neel Patel
la source
1

Ajouter

onClick = {this.delta.bind (this)}

va résoudre le problème. cette erreur survient lorsque nous essayons d'appeler la fonction de la classe ES6, nous devons donc lier la méthode.

Prashant Yalatwar
la source
1

La fonction flèche aurait pu vous faciliter la vie pour éviter de lier ce mot - clé. Ainsi:

 delta = () => {
       this.setState({
           count : this.state.count++
      });
   }
Emeka Augustine
la source
0

bien que cette question ait déjà une solution, je veux juste partager la mienne pour qu'elle soit clarifiée, j'espère qu'elle pourrait aider:

/* 
 * The root cause is method doesn't in the App's context 
 * so that it can't access other attributes of "this".
 * Below are few ways to define App's method property
 */
class App extends React.Component {
  constructor() {
     this.sayHi = 'hello';
     // create method inside constructor, context = this
     this.method = ()=> {  console.log(this.sayHi) };

     // bind method1 in constructor into context 'this'
     this.method1 = this.method.bind(this)
  }

  // method1 was defined here
  method1() {
      console.log(this.sayHi);
  }

  // create method property by arrow function. I recommend this.
  method2 = () => {
      console.log(this.sayHi);
  }
   render() {
   //....
   }
}
Kai
la source
0
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World</title>

    <script src="https://unpkg.com/[email protected]/dist/react.min.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/react-dom.min.js"></script>
    <script src="https://unpkg.com/[email protected]/babel.min.js"></script>

  </head>
  <body>
  <div id="root"></div>
    <script type="text/babel">

        class App extends React.Component{

            constructor(props){
                super(props);
                this.state = {
                    counter : 0,
                    isToggle: false
                }
            this.onEventHandler = this.onEventHandler.bind(this);   
            }

            increment = ()=>{
                this.setState({counter:this.state.counter + 1});
            }

            decrement= ()=>{
                if(this.state.counter > 0 ){
                this.setState({counter:this.state.counter - 1});    
                }else{
                this.setState({counter:0});             
                }
            }
            // Either do it as onEventHandler = () => {} with binding with this  // object. 
            onEventHandler(){
                this.setState({isToggle:!this.state.isToggle})
                alert('Hello');
            }


            render(){
                return(
                    <div>
                        <button onClick={this.increment}> Increment </button>
                        <button onClick={this.decrement}> Decrement </button>
                        {this.state.counter}
                        <button onClick={this.onEventHandler}> {this.state.isToggle ? 'Hi':'Ajay'} </button>

                    </div>
                    )
            }
        }
        ReactDOM.render(
        <App/>,
        document.getElementById('root'),
      );
    </script>
  </body>
  </html>
Ajay Kumar
la source
0

Changez simplement votre instruction bind de ce que vous avez à => this.delta = this.delta.bind (this);

Kilo One
la source
0
  1. Vérifier l'état vérifier l'état si vous créez une propriété particulière ou non

this.state = {
            name: "",
            email: ""
            }
            
           
            
this.setState(() => ({ 
             comments: comments          //comments not available in state
             })) 

2. Vérifiez le (this) si vous faites setState dans une fonction (ie handleChange) vérifiez si la fonction est liée à ceci ou si la fonction doit être une fonction de flèche.

## 3 façons de le lier à la fonction ci-dessous ##

//3 ways for binding this to the below function

handleNameChange(e) {  
     this.setState(() => ({ name }))
    }
    
// 1.Bind while callling function
      onChange={this.handleNameChange.bind(this)}
      
      
//2.make it as arrow function
     handleNameChange((e)=> {  
     this.setState(() => ({ name }))
     })
    
//3.Bind in constuctor 

constructor(props) {
        super(props)
        this.state = {
            name: "",
            email: ""
        }
        this.handleNameChange = this.handleNameChange.bind(this)
        }

K23raj
la source
0

si vous utilisez la syntaxe ES5, vous devez la lier correctement

this.delta = this.delta.bind(this)

et si vous utilisez ES6 et au- dessus , vous pouvez utiliser la fonction flèche, vous n'avez pas besoin d'utiliser bind () , il

delta = () => {
    // do something
  }
Asgar Ali Khachay
la source