setState

126 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第33天,点击查看活动详情

setState的花样用法

场景:点击按钮改变a的值。

state={a:0,b:0,c:0}
handleClick = ()=>{
    console.log(this)//App=>类组件实例
}
render(){
    return (
                    <div>
                        <h1>{this.state.a}</h1>
                         {/*onClick onChange : 合成事件 对事件进行包装 (事件代理 委托)*/}
                      <button onClick={this.handleClick}>click</button>
                    </div>
                )
            }
复制代码

方法1

this.state.a = 1
console.log(this.state.a); //1
复制代码

只改变了a的状态值,但是没有触发render渲染页面。

方法2

//3 setState : 改变值 & render

                 this.setState({

                     a:1

                 })
复制代码

使用setState改变a的状态值,且setState默认自动触发render。

方法3

 this.setState({
                     a:1
                 })

                 console.log(this.state.a); // 0
复制代码

由于setState是异步函数,所以在执行同步语句打印a时,仍然是0.但setState可以自动触发render

方法4

 this.setState({a:1})
this.setState({a:this.state.a+1},()=>{
console.log('callback',this.state.a); //1。不依赖上次setState的值。
})
console.log(this.state.a); // 0  异步原因
复制代码

在setState中写箭头函数获取异步值,在方法3的基础上回调获取。

方法5

this.setState({a:1}) //1

this.setState({a:this.state.a+2})//2

this.setState({a:this.state.a+3})//3
复制代码

所以a的值最后被更改为3. react有性能优化机制,即使render了三次a值,但内部也会等a更新完再去执行render渲染a状态。所以,即使改变了三次a值,也只执行一次render。

方法6.

this.setState({a:1})

            this.setState((preState,props)=>{

                    return {a:preState.a+1} //1

            })

            this.setState((preState,props)=>{

                    return {a:preState.a+1} //2

            })

            this.setState((preState,props)=>{

                    return {a:preState.a+1}//3

            },()=>{

                console.log('callback',this.state.a); //3

            })
复制代码

观察上一段代码,每个setState函数中都有一个回调函数返回一个状态。其实可以将上述代码理解为每一个setState依赖于上一个setState返回的状态,相当于在一个栈中,最上面的执行到最下面,且互相依赖。也很好的解决了异步问题,实现了累加依赖上次计算结果的自由。

可以将方法5和6进行对比,从异步到解决累加隐患,形成依赖。

综上所述,可以获得规律:

setState({}) log//异步,无法打印获取值,可以render值。
setState({},()=>{log}) //连续几个setState也不会产生依赖,可以打印获取值,可以render值。
setState(()=>{},()=>{log}) //连续几个setState会产生依赖,可以打印获取值。
复制代码

9.13更新

setState为同步时的情况

场景1:

  setTimeout(() => {
                    // console.log(this, "this");  //箭头函数 =》 this指向App
                    // this.setState({ a: 1 });
                    // console.log(this.state.a, "a"); //1
}, 1000)
复制代码

已知setState为异步事件,但如果发生在定时器內部,那么react就会默认成为同步时间,所以打印的结果为更改后的值。

场景2:

 handleClick2 = () => {
                console.log(this, "this");//App
                this.setState({ a: 1 })
                console.log(this.state.a, "a"); //0
            }
 <button id="button2">click2</button> 
  document.getElementById("button2"), addEventListener('click', this.handleClick2)
 
复制代码

利用原生获取dom节点触发方法时,setState也是同步方法。即打印的为更改后的值。

 handleClick2 = () => {
                console.log(this, "this");//App
                this.setState({ a: 1 })
                this.setState({ b: 1 })
                this.setState({ c: 1 })
                console.log(this.state.a, "a"); //0
            }
 <button id="button2">click2</button> 
  document.getElementById("button2"), addEventListener('click', this.handleClick2)
 
复制代码

上面代码更改了三次值,由于因为原生事件setState已经变为同步代码,所以render会重新渲染3次。

面试题:setState为同步/异步?

答案:不确定。当只用于合成事件时,生命周期钩子函数中时,为异步。

当用于定时器,用于原生dom方法时,为同步。

且,当异步时可以利用回调函数解决异步问题。