【问题标题】:Calling setState in render is not avoidable在渲染中调用 setState 是不可避免的
【发布时间】:2016-05-19 07:57:37
【问题描述】:

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

在我的例子中。我们的用户应该能够登录。登录后,我们还需要检查用户的访问权限(ajax 调用)来决定如何显示页面。代码是这样的

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

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

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

【问题讨论】:

  • 无论您拨打this.setState({ callAjax: true }),请拨打$.ajax
  • @Pavlo "callAjax" 最初为 true,我将其设置为 false,因为我不想第二次调用。或者是render和setState的无限循环
  • 能否提供一个完整的例子,包括组件的使用?
  • @Pavlo 谢谢。我想我已经知道答案了。 render 函数中的 setState 只要是异步的,就没有任何问题。例如事件监听器(正常方式)或ajax调用。我刚刚测试过,如果你直接在render.exception中调用setstate,就会抛出异常。如果你把它包装在一个 seTimeout 回调中。它很好
  • 这里是组件facebook.github.io/react/tips/initial-ajax.html中正确AJAX调用的示例

标签: asynchronous reactjs reactive-programming


【解决方案1】:

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

相反,想想你的组件可能处于的各种状态——就像你在为一个状态机建模(事实证明你就是这样):

  • 初始状态(用户未点击按钮)
  • 等待授权(用户点击登录,但我们还不知道 Ajax 请求的结果)
  • 用户可以访问某些东西(我们已经获得了 Ajax 请求的结果)

使用组件的状态对此进行建模,一切顺利。

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

【讨论】:

  • 看起来确实是一个更广泛和完整的答案。试图删除我原来的答案,但不能,因为它被标记为接受的答案:(
  • @wintvelt 哦,你的答案的重点是在另一个ajax调用的成功回调中调用ajax,这样就不需要使用'login'这样的额外状态。但是,我有点歪曲了我的情况,如果用户已经存储在本地存储中的登录凭据。我们可以通过 Parsen.User.current() 检查它(Parser 是一个即将退役的 sdk)。在您的解决方案中,如果用户已经登录只需要检查访问权限?所以应该有一个状态来区分用户是否登录(解析sdk可以检查localstorage)如果用户登录,调用ajax自动检查访问
  • @wintvelt 但你的想法确实启发了我。这是解决方案:在 componentDidMount 中,如果用户登录,则调用 ajax 来检查访问权限,否则什么都没有。当用户首先单击按钮 .login 时,在登录回调中,调用ajax检查用户访问!!!
  • 如果ajax触发后台任务怎么办?
猜你喜欢
  • 2019-08-14
  • 1970-01-01
  • 2019-11-21
  • 2017-06-08
  • 2019-10-17
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
  • 1970-01-01
相关资源
最近更新 更多