Le document React indique que la fonction render
doit être pure ce qui signifie qu'elle ne doit pas utiliser this.setState
. Cependant, je crois que lorsque l'état dépend de "distant", c'est-à-dire le résultat d'un appel ajax. La seule solution est setState()
à l'intérieur d'une fonction render
Dans mon cas.Nos utilisateurs devraient pouvoir se connecter. Après la connexion, nous devons également vérifier l'accès de l'utilisateur (appel ajax) pour décider comment afficher la page.Le code est quelque chose comme ça
React.createClass({
render:function(){
if(this.state.user.login)
{
//do not call it twice
if(this.state.callAjax)
{
var self=this
$.ajax{
success:function(result)
{
if(result==a)
{self.setState({callAjax:false,hasAccess:a})}
if(result==b)
{self.setState({callAjax:false,hasAccess:b})}
}
}
}
if(this.state.hasAccess==a) return <Page />
else if(this.state.hasAccess==a) return <AnotherPage />
else return <LoadingPage />
}
else
{
return <div>
<button onClick:{
function(){this.setState({user.login:true})}
}>
LOGIN
</button>
</div>
}
}
})
L'appel ajax ne peut pas apparaître dans componentDidMount
car lorsque l'utilisateur clique sur le bouton CONNEXION, la page est à nouveau rendue et nécessite également un appel ajax. Je suppose donc que le seul endroit où setState
se trouve à l'intérieur du render
qui enfreint le principe React
De meilleures solutions? Merci d'avance
render
devrait toujours rester pur. C'est une très mauvaise pratique d'y faire des effets secondaires, et appeler setState
est un gros drapeau rouge; dans un exemple simple comme celui-ci, cela peut fonctionner correctement, mais c'est la voie vers des composants hautement non maintenables, et cela ne fonctionne que parce que l'effet secondaire est asynchrone.
Au lieu de cela, pensez aux différents états dans lesquels votre composant peut être - comme vous modélisiez une machine à états (ce qui, en fait, vous l'êtes):
Modélisez cela avec l'état de votre composant et vous êtes prêt à partir.
React.createClass({
getInitialState: function() {
return {
busy: false, // waiting for the ajax request
hasAccess: null, // what the user has access to
/**
* Our three states are modeled with this data:
*
* Pending: busy === true
* Has Access: hasAccess !== null
* Initial/Default: busy === false, hasAccess === null
*/
};
},
handleButtonClick: function() {
if (this.state.busy) return;
this.setState({ busy: true }); // we're waiting for ajax now
this._checkAuthorization();
},
_checkAuthorization: function() {
$.ajax({
// ...,
success: this._handleAjaxResult
});
},
_handleAjaxResult: function(result) {
if(result === a) {
this.setState({ hasAccess: a })
} else if(result ===b ) {
this.setState({ hasAccess: b })
}
},
render: function() {
// handle each of our possible states
if (this.state.busy) { // the pending state
return <LoadingPage />;
} else if (this.state.hasAccess) { // has access to something
return this._getPage(this.state.hasAccess);
} else {
return <button onClick={this.handleButtonClick}>LOGIN</button>;
}
},
_getPage: function(access) {
switch (access) {
case a:
return <Page />;
case b:
return <AnotherPage />;
default:
return <SomeDefaultPage />;
}
}
});