Calling setState in render is not avoidable

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

6

1 Answer

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

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like