在渲染中调用 setState 是不可避免的 [英] Calling setState in render is not avoidable
问题描述
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屋!