web-dev-qa-db-fra.com

L'appel de setState dans le rendu n'est pas évitable

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

9
Guichi

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):

  • L'état initial (l'utilisateur n'a pas cliqué sur le bouton)
  • Autorisation en attente (l'utilisateur a cliqué sur la connexion, mais nous ne savons pas encore le résultat de la demande Ajax)
  • L'utilisateur a accès à quelque chose (nous avons le résultat de la requête Ajax)

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 />;
    }
  }
});
19
Michelle Tilley