在 $.ajax() 请求后反应 setState() 不更新状态 [英] React setState() not updating state after $.ajax() request

查看:38
本文介绍了在 $.ajax() 请求后反应 setState() 不更新状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 react-router.在 IndexRoute 上使用 onEnter 异步钩子检查身份验证后,App 组件被渲染.App 组件有一个初始状态 auth,它在呈现时设置为 undefined.auth 状态作为 prop 传递给 Navbar 组件,用于决定是否显示登录、注册和注销链接.

App 组件渲染完成后,componentDidMount() 会调用ajax 再次检查用户是否通过身份验证.在响应时,它会更改状态.从 ajax 请求更改状态后,我将状态记录到控制台,this.setState() 方法未更改状态,但仍以某种方式触发 Navbar 组件和 this.props 上的 componentWillReceiveProps() 方法.auth 值仍未定义.

//异步检查身份验证isAuthenticated(nextState, replace, callback) {$.ajax({类型:'获取',网址:'/认证',成功:功能(资源){如果(!res){callback(replace({ pathname: '/login', query: { auth: 'false' } }));}别的{打回来();}}});};//路线无功路线 = (<路由器历史={browserHistory}><Route path="/" component={require('./components/app')}><IndexRoute component={require('./components/dashboard/index')} onEnter={Auth.isAuthenticated}/><路由路径=/注册"component={require('./components/authentication/register')}onEnter={Auth.isNotAuthenticated}/><路由路径=/登录"component={require('./components/authentication/login')}onEnter={Auth.isNotAuthenticated}/><路由路径="*"组件={require('./components/404/404')}/></路线></路由器>);//应用程序const App = React.createClass({getInitialState(){返回 {身份验证:未定义}},componentDidMount(){console.log('App componentDidMount');this.checkAuth();},checkAuth(){var self = this;$.ajax({类型:'获取',网址:'/认证',成功:功能(资源){如果(资源){self.setState({身份验证:真实});}别的{self.setState({ auth : false});}}});控制台日志(this.state.auth);},使成为() {返回(<div className="appWrapper"><Navbar auth={this.state.auth}/><div className="容器">{this.props.children}

);}});//导航栏var Navbar = React.createClass({getInitialState(){返回{用户操作: '' ,身份验证:this.props.auth}},componentDidMount(){console.log('Navbar componentDidMount', this.props.auth);this.checkAuthState();},componentWillReceiveProps(){console.log('Navbar componentWillReceiveProps', this.props.auth);this.setState({身份验证:this.props.auth});this.checkAuthState();},checkAuthState(){console.log('Nav Mounted with auth : ', this.state.auth);如果(this.state.auth == 未定义){this.state.user_actions = '';}如果(!this.state.auth){this.state.user_actions = <ul className="nav navbar-nav navbar-right"><li><a href="/login">登录</a></li><li><a href="/register">注册</a></li></ul>;this.setState({user_actions : this.state.user_actions});}如果(this.state.auth){this.state.user_actions = <ul className="nav navbar-nav navbar-right"><li><a href="/logout">注销</a></li></ul>;this.setState({user_actions : this.state.user_actions});}},渲染:函数(){返回 (<nav className="navbar navbar-default"><div className="容器"><a href="/" className="navbar-brand">Reactor</a>{this.state.user_actions}

</nav>);}});

解决方案

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

<块引用>

  1. 永远不要直接改变 this.state,而是使用 setState 方法.(行:108, 111, 121, 133, 136, 146)
  2. 您应该使用状态来存储随时间变化的数据,而不是一个元素.(第 111、121、136、146 行)

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

1.Ajax 响应正在更改状态值,但日志中的值并未更改.

如果你在ajax请求后打印值你就看不到了!原因是:

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

$.ajax({ ...,成功:功能(资源){if(res) { self.setState({ auth : true });}/...}//稍后会执行(在ajax得到响应之后)});控制台日志(this.state.auth);//将首先执行,这就是为什么它总是将值打印为 undefined

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

this.setState({ auth: true});控制台日志(this.state.auth);//将打印 false,而不是 true 作为您的新值

您可以使用 componentWillUpdate(nextProps, nextState) 方法查看新的状态值.你可以从这个链接中了解这一点:React.JS 组件规范和生命周期

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

这意味着您的状态值已通过 setState() 在您的 ajax 响应中成功更改.证明是 Navbar 组件接收到一个新的 props,该 props 由 App 组件(其中 auth 状态改变)发送下来,这将触发 componentWillReceiveProps() 方法.

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

//应用const App = React.createClass({获取初始状态:函数(){返回 {身份验证:假}},componentDidMount :函数(){console.log('App componentDidMount');this.checkAuth();},componentWillUpdate :函数(nextProps,nextState){//你会在这里看到不断变化的状态值console.log('您的上一个身份验证状态:' + this.state.auth);console.log('你的下一个身份验证状态:' + nextState.auth);},检查验证:函数(){var self = this;$.ajax({类型:'获取',网址:'/认证',成功:功能(资源){如果(资源){self.setState({ auth : true });}}});},渲染:函数(){返回(

<Navbar auth={this.state.auth}/><div className="容器">{this.props.children}

);}});//导航栏//因为 navbar 组件通过 props 从父(app)接收数据(this.props.auth),所以我们不再需要在 Navbar 组件中分配 auth 作为状态.const Navbar = React.createClass({渲染:函数(){//你不再需要 checkAuthState 方法让导航项;如果(!this.props.auth){navItems = (

</nav>);}});

希望有帮助!

I'm using react with react-router. After checking authentication with onEnter Asynchronous hook on IndexRoute, App component gets rendered. App component has an initial state auth which is set to undefined when it renders. auth state is being passed to Navbar component as prop where it will be used to decide whether or not to show login, register and logout links.

When App component is done rendering, componentDidMount() makes an ajax call to again check if user is authenticated. On response it makes change to the state. After state change from ajax request, i'm logging state to console, this.setState() method is not changing state but somehow still triggers componentWillReceiveProps() method on Navbar component and this.props.auth value is still undefined.

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

解决方案

First of all, I suggest you to reread React.JS documentation, because there are couple of things that need to be noted:

  1. Never mutate this.state directly, use setState method instead. (line: 108, 111, 121, 133, 136, 146)
  2. You should use state for storing data that changes over time, not an element. (line: 111, 121, 136, 146)

tl;dr; Let's go back to the questions:

1. Ajax response is changing a state value, but the value isn't change in your log.

You won't see it if you print the value after ajax request! The reason is:

First, you're doing asynchronous request using Ajax and trying to see the result in synchronous way. JS will execute your console.log first which still contains the value before request, and then perform ajax request callback. This is the block of your code:

$.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

Second, you won't able to see the changed state value right after you set a new state value. For instance, let say the value of this.state.auth is false:

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

You're able to see your new state value by using componentWillUpdate(nextProps, nextState) method. You can read about this from this link: React.JS Component Specs and Lifecycle

2. Still triggers componentWillReceiveProps() method on Navbar component and this.props.auth value is still undefined.

It means that your state value is successfully changed by setState() on your ajax response. The proof is Navbar component receive a new props that send it down by App component (where the auth state is changed) which will trigger componentWillReceiveProps() method.

Maybe your code should be like this:

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

Hope it helps!

这篇关于在 $.ajax() 请求后反应 setState() 不更新状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆