在渲染中调用 setState 是不可避免的 [英] Calling setState in render is not avoidable

查看:22
本文介绍了在渲染中调用 setState 是不可避免的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

React 文档声明 render 函数应该是 pure 这意味着它不应该使用 this.setState在其中.但是,我相信当状态依赖于远程"时,即来自 ajax 调用的结果.唯一的解决方案是 render 函数内的 setState()

>

在我的情况下,我们的用户应该可以登录.登录后,我们还需要检查用户的访问权限(ajax调用)来决定如何显示页面.代码是这样的

React.createClass({渲染:函数(){如果(this.state.user.login){//不要调用它两次如果(this.state.callAjax){var self=this$.ajax{成功:功能(结果){如果(结果== a){self.setState({callAjax:false,hasAccess:a})}如果(结果== b){self.setState({callAjax:false,hasAccess:b})}}}}if(this.state.hasAccess==a) return else if(this.state.hasAccess==a) return 否则返回 }别的{返回 

<点击按钮:{function(){this.setState({user.login:true})}}>登录

}}})

ajax 调用不能出现在 componentDidMount 中,因为当用户单击 LOGIN 按钮时,页面会重新呈现并且还需要 ajax 调用.所以我想唯一的地方是 setState 位于 render 函数内部,违反了 React 原则

有更好的解决方案吗?提前致谢

解决方案

render 应该始终保持纯净.在那里做副作用的事情是一个非常糟糕的做法,调用 setState 是一个很大的危险信号;在像这样的简单示例中,它可以正常工作,但它是通往高度不可维护组件的道路,而且它只能工作,因为副作用是异步的.

相反,请考虑您的组件可能处于的各种状态 - 就像您正在为状态机建模(事实证明,您是这样的):

  • 初始状态(用户没有点击按钮)
  • 待授权(用户点击登录,但我们还不知道 Ajax 请求的结果)
  • 用户有权访问某些内容(我们已获得 Ajax 请求的结果)

用您的组件的状态对此进行建模,然后就可以开始了.

React.createClass({getInitialState:函数(){返回 {busy: false,//等待 ajax 请求hasAccess: null,//用户可以访问的内容/*** 我们的三个州是用这些数据建模的:** 待定:忙 === 真* 有访问权限:hasAccess !== null* 初始值/默认值:busy === false,hasAccess === null*/};},处理按钮点击:函数(){如果(this.state.busy)返回;this.setState({busy: true });//我们现在正在等待 ajaxthis._checkAuthorization();},_checkAuthorization:函数(){$.ajax({//...,成功:this._handleAjaxResult});},_handleAjaxResult:函数(结果){如果(结果 === a){this.setState({ hasAccess: a })} else if(结果 ===b ) {this.setState({ hasAccess: b })}},渲染:函数(){//处理我们每个可能的状态if (this.state.busy) {//挂起状态返回 <LoadingPage/>;} else if (this.state.hasAccess) {//可以访问某些东西返回 this._getPage(this.state.hasAccess);} 别的 {return ;}},_getPage:函数(访问){开关(访问){情况一:返回<页面/>;案例b:返回 <AnotherPage/>;默认:返回 <SomeDefaultPage/>;}}});

React document states that the render function should be pure which mean it should not use this.setState in it .However, I believe when the state is depended on 'remote' i.e. result from ajax call.The only solution is setState() inside a render function

In my case.Our users can should be able to log in. After login, We also need check the user's access (ajax call )to decide how to display the page.The code is something like this

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

The ajax call can not appear in componentDidMount because when user click LOGIN button the page is re-rendered and also need ajax call .So I suppose the only place to setState is inside the render function which breach the React principle

Any better solutions ? Thanks in advance

解决方案

render should always remain pure. It's a very bad practice to do side-effecty things in there, and calling setState is a big red flag; in a simple example like this it can work out okay, but it's the road to highly unmaintainable components, plus it only works because the side effect is async.

Instead, think about the various states your component can be in — like you were modeling a state machine (which, it turns out, you are):

  • The initial state (user hasn't clicked button)
  • Pending authorization (user clicked login, but we don't know the result of the Ajax request yet)
  • User has access to something (we've got the result of the Ajax request)

Model this out with your component's state and you're good to go.

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

这篇关于在渲染中调用 setState 是不可避免的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆