在$ .ajax()请求之后,反应setState()不更新状态

时间:2016-04-15 13:16:03

标签: reactjs react-router

我使用react-react进行反应。在使用IndexRoute上的onEnter Asynchronous挂钩检查身份验证后,将呈现App组件。应用程序组件具有初始状态auth,在呈现时设置为undefined。 auth状态被传递给Navbar组件作为prop,它将用于决定是否显示登录,注册和注销链接。

当App组件完成呈现时,componentDidMount()进行ajax调用以再次检查用户是否经过身份验证。在回应时它会改变状态。在从ajax请求状态更改后,我将日志状态更改为控制台,this.setState()方法不会更改状态,但仍会以某种方式触发Navbar组件上的componentWillReceiveProps()方法,并且this.props.auth值仍未定义。< / p>

// Checks Authentication Asynchronously 
isAuthenticated(nextState, replace, callback) {
    $.ajax({
        type : 'GET',
        url : '/auth',
        success : function(res){
            if(!res){
                callback(replace({ pathname: '/login', query: { auth: 'false' } }));
            }else{
                callback();
            }
        }
    });
};

// routes
var routes = (
    <Router history={browserHistory}>
        <Route path="/" component={require('./components/app')}>
            <IndexRoute component={require('./components/dashboard/index')} onEnter={Auth.isAuthenticated}/>

            <Route path="/register"
                   component={require('./components/authentication/register')}
                   onEnter={Auth.isNotAuthenticated} />

            <Route path="/login"
                   component={require('./components/authentication/login')}
                   onEnter={Auth.isNotAuthenticated}/>

            <Route path="*"
                   component={require('./components/404/404')}/>
        </Route>
    </Router>
);

// App
const App = React.createClass({

    getInitialState(){
        return {
            auth : undefined
        }
    },

    componentDidMount(){
        console.log('App componentDidMount');
        this.checkAuth();
    },

    checkAuth(){
        var self = this;
        $.ajax({
            type : 'GET',
            url : '/auth',
            success : function(res){
                if(res){
                    self.setState({
                        auth : true
                    });
                }else{
                    self.setState({ auth : false});
                }
            }
        });
        console.log(this.state.auth);
    },

    render() {
        return(
            <div className="appWrapper">
                <Navbar auth={this.state.auth}/>

                <div className="container">
                    {this.props.children}
                </div>
            </div>
        );
    }
});

// Navbar
var Navbar = React.createClass({

    getInitialState(){
        return{
            user_actions : '' ,
            auth : this.props.auth
        }
    },

    componentDidMount(){
        console.log('Navbar componentDidMount ', this.props.auth);
        this.checkAuthState();
    },

    componentWillReceiveProps(){
        console.log('Navbar componentWillReceiveProps ', this.props.auth);
        this.setState({
            auth : this.props.auth
        });
        this.checkAuthState();
    },

    checkAuthState(){
        console.log('Nav Mounted with auth : ', this.state.auth);

        if(this.state.auth == undefined){
            this.state.user_actions = '';
        }
        if(!this.state.auth){
            this.state.user_actions =   <ul className="nav navbar-nav navbar-right">
                <li><a href="/login">Login</a></li>
                <li><a href="/register">Register</a></li>
            </ul>;
            this.setState({
                user_actions : this.state.user_actions
            });
        }

        if(this.state.auth){
            this.state.user_actions =   <ul className="nav navbar-nav navbar-right">
                <li><a href="/logout">Logout</a></li>
            </ul>;
            this.setState({
                user_actions : this.state.user_actions
            });
        }
    },

    render : function(){
        return (
            <nav className="navbar navbar-default">
                <div className="container">
                    <a href="/" className="navbar-brand">Reactor</a>
                    {this.state.user_actions}
                </div>
            </nav>
        );
    }
});

4 个答案:

答案 0 :(得分:21)

首先,我建议你重读React.JS文档,因为有几件事需要注意:

  
      
  1. 不要直接改变this.state,而是使用setState方法。 (line: 108, 111, 121, 133, 136, 146)
  2.   
  3. 您应该使用state来存储随时间变化的数据,而不是元素。 (line: 111, 121, 136, 146)
  4.   

TL;博士; 让我们回到问题:

1。 Ajax响应正在改变状态值,但是日志中的值不会发生变化。

如果您在ajax请求后打印该值,您将无法看到它!原因是:

首先,您使用Ajax执行异步请求并尝试以同步方式查看结果。 JS将首先执行您的console.log,它仍然包含请求之前的值,然后执行ajax请求回调。这是您的代码块:

$.ajax({ ...,
    success: function(res) {
        if(res) { self.setState({ auth : true }); }/
        ...
    }  // will executed later (after ajax get response)
 });
 console.log(this.state.auth); // will executed first, this is why it always prints the value as undefined

其次,在设置新状态值后,您无法立即看到更改后的状态值。例如,假设this.state.auth的值为false

this.setState({ auth: true});
console.log(this.state.auth); // will print false, instead of true as your new value 

您可以使用componentWillUpdate(nextProps, nextState)方法查看新的州值。您可以通过以下链接了解此信息:React.JS Component Specs and Lifecycle

2。仍然触发Navbar组件上的componentWillReceiveProps()方法,并且this.props.auth值仍未定义。

这意味着您的ajax响应上的setState()成功更改了您的状态值。证据是Navbar组件接收一个新的道具,它通过App组件发送它(其中auth状态被更改),这将触发componentWillReceiveProps()方法。

也许你的代码应该是这样的:

// App
const App = React.createClass({
    getInitialState : function(){
        return {
            auth : false
        }
    },

    componentDidMount : function() {
        console.log('App componentDidMount');
        this.checkAuth();
    },

    componentWillUpdate : function(nextProps, nextState) {
        //you'll see the changing state value in here
        console.log('Your prev auth state: ' + this.state.auth);
        console.log('Your next auth state: ' + nextState.auth);
    },

    checkAuth : function(){
        var self = this;
        $.ajax({
            type : 'GET',
            url : '/auth',
            success : function(res){
                if(res){
                    self.setState({ auth : true });
                }
            }
        });
    },

    render : function(){
        return(
            <div className="appWrapper">
                <Navbar auth={this.state.auth}/>
                <div className="container">
                    {this.props.children}
                </div>
            </div>
        );
    }
});

// Navbar
// Because the navbar component receive data (this.props.auth) from parent (app) via props, so we're no longer need to assign auth as a state in Navbar component. 
const Navbar = React.createClass({
    render : function(){
        // you're no longer need checkAuthState method
        let navItems;
        if(!this.props.auth){
            navItems =  (<ul className="nav navbar-nav navbar-right">
                <li><a href="/login">Login</a></li>
                <li><a href="/register">Register</a></li>
            </ul>);
        } else {
            navItems =  (<ul className="nav navbar-nav navbar-right">
                <li><a href="/logout">Logout</a></li>
            </ul>);
        }

        return (
            <nav className="navbar navbar-default">
                <div className="container">
                    <a href="/" className="navbar-brand">Reactor</a>
                    { navItems }
                </div>
            </nav>
        );
    }
});

希望它有所帮助!

答案 1 :(得分:1)

在ajax范围内。它无法访问反应状态。作为替代方案,您可以将模块中的其他方法称为ajax成功调用,然后在那里更新状态。 按照这个例子。

var reactModule = React.createClass({
   getInitialState:function(){

    },
  render: function() {
    return (      
           <div>
            content
          </div>
    );
  },
componentDidMount: function() {
     var ajaxSuccess=this.ajaxSuccess;

               $.ajax({
                   type: "POST",
                   url: $api_url + 'index.php/app/icon1_update',
                   dataType: "text",
                   data:fd,
                   contentType: false,
                   processData: false,
                   success: ajaxSuccess
               });  
     }, 
ajaxSuccess:function(e){
   //e is the result. update state here.
}  
});

答案 2 :(得分:0)

请查看componentWillReceiveProps的文档:

componentWillReceiveProps(
  object nextProps
)

https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops

当您的属性发生变化时,访问属性“nextProps”。否则,您将访问旧属性。

作为一个小提示: 在render方法中包含checkAuthState()代码,而不是在componentDidMount方法中,因为您可以避免使用setState调用。

答案 3 :(得分:0)

只需使用箭头功能即可访问“此”:

success: () => {
this.setState({ data: value })
}
相关问题