React中的setState方法究竟是同步还是异步?

        关于这个问题,答案无非就两种,要么是同步,要么是异步,哈哈哈哈!!!

        好了,不卖关子了,这个问题的答案就是:

setState更新数据状态是异步的,但也有同步的情况(17版本中)

        下面让我们来看看到底是怎么回事:

        首先,有一个需求:state中num的初始值为1,通过点击按钮实现num加一的操作:

        我们先用18的版本来测试一段代码

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    //react18版本
    <script src="./lib/react.development.js"></script>
    <script src="./lib/react-dom.development.js"></script>
    <script src="./lib/babel.min.js"></script>
</head>

<body>
    <div id="app"></div>
    <script type="text/babel">
        // 在React中更新数据的setState是异步的
        class App extends React.Component{
            state={num:1}
            add = ()=>{
                this.setState({num:this.state.num+1})
                console.log(this.state.num)//1
                }
            render(){
                return(
                    <div>
                        <button onClick={this.add}>{this.state.num}</button>    
                    </div>
                )
            }
        }
        const app = ReactDOM.createRoot(document.getElementById('app'))
        app.render(<App/>)
    </script>
</body>

</html>

运行结果:

结论: 此时界面上变成了2,但打印的结果还是1,由此可以看出setState更新数据状态是异步的

接下来,我们再把需求改一下:在之前的基础上点击一次连续加三次1

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    //react18版本
    <script src="./lib/react.development.js"></script>
    <script src="./lib/react-dom.development.js"></script>
    <script src="./lib/babel.min.js"></script>
</head>

<body>
    <div id="app"></div>
    <script type="text/babel">
        class App extends React.Component{
            state={num:1}
            add = ()=>{
                this.setState({num:this.state.num+1})
                console.log('1',this.state.num)//1
                this.setState({num:this.state.num+1})
                console.log('2',this.state.num)//1
                this.setState({num:this.state.num+1})
                console.log('3',this.state.num)//1
            }
            render(){
                return(
                    <div>
                        <button onClick={this.add}>{this.state.num}</button>    
                    </div>
                )
            }
        }
        const app = ReactDOM.createRoot(document.getElementById('app'))
        app.render(<App/>)
    </script>
</body>

</html>

运行结果:

此时页面只更新了一次,控制台打印三次1,说明:setState可以调用多次,会集中一次更新。

那么我们如何得到更新后的数据呢?

通过查找官方文档,setState接收两个参数,第一个:对象/函数 第二个:数据修改并且dom更新后的回调函数

证明:

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./lib/react.development.js"></script>
    <script src="./lib/react-dom.development.js"></script>
    <script src="./lib/babel.min.js"></script>
</head>

<body>
    <div id="app"></div>
    <script type="text/babel">
        class App extends React.Component{
            state={num:1}
            add = ()=>{
                this.setState({num:this.state.num+1},()=>{
                    // state中的数据
                    console.log('setState回调',this.state.num);//2
                    // 按钮中的内容
                    console.log('dom',document.querySelector('button').innerText);//2
                })
                console.log(this.state.num);//1
            }
            render(){
                return(
                    <div>
                        <button onClick={this.add}>{this.state.num}</button>    
                    </div>
                )
            }
        }
        const app = ReactDOM.createRoot(document.getElementById('app'))
        app.render(<App/>)
    </script>
</body>

</html>

运行结果:

 此时回调函数中打印的结果以及按钮中的文本也是最新的,说明回调函数是在数据及dom更新完之后随之更新的,那么我们也可以在该回调函数中继续调用setState更新状态

但是,当我们写这种需求时,比如:要在原来状态的基础上进行操作的话,建议使用setState(函数)

官方建议:如果要在原来状态的基础上进行操作的话,建议使用setState(函数)

喔喔喔,原来setState第一个参数也可以是函数呀,那就试试呗

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./lib/react.development.js"></script>
    <script src="./lib/react-dom.development.js"></script>
    <script src="./lib/babel.min.js"></script>
</head>

<body>
    <div id="app"></div>
    <script type="text/babel">
        // 在React中更新数据的setState是异步的
        class App extends React.Component{
            state={num:1}

            //官方:如果要在原来状态的基础上进行操作的话,建议使用setState(函数)
            add = ()=>{
                this.setState(prevState=>{
                    return{
                        num:prevState.num+1
                    }
                })
                this.setState(prevState=>{
                    return{
                        num:prevState.num+1
                    }
                },()=>{
                    console.log(this.state.num);
                })
                
            }
            render(){
                return(
                    <div>
                        <button onClick={this.add}>{this.state.num}</button>    
                    </div>
                )
            }
        }
        const app = ReactDOM.createRoot(document.getElementById('app'))
        app.render(<App/>)
    </script>
</body>

</html>

运行结果:

 此时,界面和打印结果都是最新的。因此,我们以后在遇到此种场景的时候就需要这样以函数的形式来使用

那么setState更新数据状态都是异步的吗?

这里我们使用react17测试一下

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    //react17版本
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="./lib/babel.min.js"></script>
</head>

<body>
    <!-- 在react17中,修改状态可能是同步的 -->
    <div id="app"></div>
    <script type="text/babel">
        class App extends React.Component{
            state = {num:1}
            add = ()=>{
                this.setState({num:this.state.num+1})
                console.log(this.state.num);//1
            }
            render(){
                return (
                    <div>
                        <button onClick={this.add}>{this.state.num}</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<App/>,document.getElementById('app'))
    </script>
</body>

</html>

这里打印结果还是1,说明还是异步的

当使用定时器时:

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="./lib/babel.min.js"></script>
</head>

<body>
    <!-- 在react17中,修改状态可能是同步的 -->
    <div id="app"></div>
    <script type="text/babel">
        class App extends React.Component{
            state = {num:1}
            add = ()=>{
                // 1.定时器
                 setTimeout(()=>{
                     this.setState({num:this.state.num+1})
                     console.log(this.state.num);//2
                 })
            }
            render(){
                return (
                    <div>
                        <button onClick={this.add}>{this.state.num}</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<App/>,document.getElementById('app'))
    </script>
</body>

</html>

运行结果:

 由此可见,在定时器的情况下更新状态是同步的

当时用Promise时:

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="./lib/babel.min.js"></script>
</head>

<body>
    <!-- 在react17中,修改状态可能是同步的 -->
    <div id="app"></div>
    <script type="text/babel">
        class App extends React.Component{
            state = {num:1}
            add = ()=>{
                
                // 2.Promise中
                Promise.resolve().then(()=>{
                    this.setState({num:this.state.num+1})
                    console.log(this.state.num);//2
                })
            }
            render(){
                return (
                    <div>
                        <button onClick={this.add}>{this.state.num}</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<App/>,document.getElementById('app'))
    </script>
</body>

</html>

运行结果:

 由此可见,在使用Promise的情况下更新状态也是同步的

总结:

React更新数据状态是异步的,但是在react17中可能存在同步的情况(定时器,Promise)

好了,以上就更新状态同步和异步的情况已经做了详细说明,关于setState更新数据状态的答案想必大家都已经理解了吧,下次有人问你的时候,你可以理直气壮地讲给他听啦!!!!

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是个程序员吧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值