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 !
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.
0 votes
Je sais :p c'est juste que je préfère le bind à la place. C'est sale mais ça marche..
0 votes
@MayankShukla, je ne suis pas sûr de savoir pourquoi l'erreur est comme ça au lieu de
Cannot read property 'state' of undefined
?0 votes
Je n'aime pas le bind, parce que si vous avez beaucoup de méthodes, vous aurez beaucoup de copypast comme ceci
this.addUser = this.addUser.bind(this)
dans un constructeur.0 votes
@Mr.Alien mais je pense que c'est la meilleure façon de maintenir le contexte parce que dans le cas de plusieurs fonctions, si nous mettons tous les liens à l'intérieur d'un constructeur inutile, cela augmente le nombre de lignes :)
0 votes
@MayankShukla Yep pas de refus :)
0 votes
@Andrew l'erreur est Impossible de lire la propriété 'users' d'undefined Je pense que .
0 votes
Si j'écris addUser = (userName) => { .... alors l'erreur est index.js:3 Uncaught Error : Impossible de trouver le module "./App", cela casse tout.
0 votes
@MayankShukla, est-ce que cela signifie en
this.state.users
nous l'avons définie, mais sansstate
classée ?3 votes
@IvanTopic met cette ligne dans le constructeur :
this. addUser = this. addUser.bind(this)
cette syntaxe est la syntaxe de l'initialisateur de propriété son a ceci est expérimental syntaxe vous devez utiliser le plugin babel pour cela.0 votes
Merci, cela fonctionne. Btw J'ai bien Babel, je joue avec ceci github.com/krasimir/react-webpack-starter