避不开的 setState 问题

开发 前端
在开始讲解异步特性之前,我们需要先明确,从 API 层面上说,它就是普通的调用执行的函数 ,自然是同步 API 。因此,这里所说的同步和异步,指的是 API 调用后,state 的改变或者更新 DOM 的时机,是同步还是异步的。

[[422711]]

本文转载自微信公众号「勾勾的前端世界」,作者西岭 。转载本文请联系勾勾的前端世界公众号。

异步的 setState

“setState 是异步还是同步的?”

这是一个面试中经常会被问到的经典面试题。

在开始讲解异步特性之前,我们需要先明确,从 API 层面上说,它就是普通的调用执行的函数 ,自然是同步 API 。因此,这里所说的同步和异步,指的是 API 调用后,state 的改变或者更新 DOM 的时机,是同步还是异步的。

我们先来看一段代码:

  1. import React, { Component } from'react' 
  2.  
  3. exportclass States extends Component { 
  4.   state = { 
  5.     name:"lisi"
  6.     age:18 
  7.   } 
  8.  
  9.   fun= ()=>{ 
  10.     this.setState({ 
  11.       name:'xiling' 
  12.     }) 
  13.     console.log(this.state.name
  14.   } 
  15.  
  16.   render() { 
  17.     return ( 
  18.       <div> 
  19.         <h2>State 组件</h2> 
  20.         <p>{this.state.name}</p> 
  21.         <buttononClick={()=>{this.fun()}}>点我</button> 
  22.       </div> 
  23.     ) 
  24.   } 
  25.  
  26. exportdefault States 

代码实现的的功能非常简单,就是在点击按钮后,修改 state 中的 name 属性值。事件处理函数中,是在调用 setState 方法之后,又将 state 的值打印到了控制台。运行代码你会发现, Dom 中的值发生了改变,但是控制台却是之前的结果值。

如果你对异步的运行规则比较熟悉,你一定不会感到奇怪。很显然,this.setState() 是异步执行的,调用之后,后面的代码就紧跟着执行了,因此,控制台打印的结果肯定是修改之前。

因此,我们可以确定,this.setState() 确实是异步调用执行的代码。

注意,此时,你可以将 this.setState() 当作普通的异步执行代码(再次强调 JSX 就是 JS)。

那么,如果我想调整代码获取异步执行结果,应该怎么做呢?

既然可以看作为普通的异步代码,想想也知道,其实 this.setState() 方法还提供了第二个参数,我们可以传入一个回调函数,这个函数就是异步结束后执行的回调函数,具体代码修改如下:

  1. fun=async ()=>{ 
  2.     awaitthis.setState({ 
  3.       name:'xiling' 
  4.     },()=>{ 
  5.       console.log(this.state.name
  6.     }) 
  7.   } 

回调函数处理异步结果本来就是传统的异步编码方式,但是,我们也知道,异步中的回调函数有一个最大的问题就是 “回调地狱”,那么既然是异步,我们是不是就可以封装一个 Promise 了呢?

当然可以!

但是,封装一个 Promise 就显得有些复杂。最简单的方式就是借助 ES 2017 中异步调用函数 async/ await 来实现。我们直接修改事件处理函数即可,具体语法规则如下:

  1. fun=async ()=>{ 
  2.     awaitthis.setState({ 
  3.       name:'xiling' 
  4.     }) 
  5.     console.log(this.state.name
  6.   } 

setState 的执行逻辑

在使用 this.setState() 进行状态更改时,需要进行逻辑处理应该怎么做呢?

其实 this.setState() 的第一个参数是可以接收一个函数处理的。需要注意的是,函数的运行必须返回一个 state 对象,具体代码如下:

  1. fun = ()=>{ 
  2.     this.setState((state)=>{ 
  3.       // 函数逻辑代码 …… 
  4.       let returnData 
  5.       returnData = state.age+1 
  6.       // 最终返回一个state 对象 
  7.       return { 
  8.         age:returnData 
  9.       } 
  10.     }) 
  11.   } 

也就是说 this.setState() 既可以接收对象参数,也可以接收一个处理函数。

那么,这两者又有什么区别呢?

在时间处理函数中,我们分别使用两次 this.setState() 对 state 进行修改操作。

两次函数执行操作:

  1. fun = ()=>{ 
  2.     this.setState((state)=>{ 
  3.       return { 
  4.         age:state.age+2 
  5.       } 
  6.     }) 
  7.  
  8.     this.setState((state)=>{ 
  9.       return { 
  10.         age:state.age+3 
  11.       } 
  12.     }) 
  13.   } 

运行代码可以发现,两次 this.setState() 中的函数都得到了执行,修改了 state 的值。我们再将同样的代码逻辑,使用对象数据的方式进行修改操作:

  1. fun = ()=>{ 
  2.     this.setState({ 
  3.       age:this.state.age+2 
  4.     }) 
  5.     this.setState({ 
  6.       age:this.state.age+3 
  7.     }) 
  8.   } 

结果显示,只有最后一次 this.setState() 得到了执行。

没错,this.setState() 如果是函数,那么函数会依次从上往下执行;而如果是一个对象, React 会将多次 this.setState() 的调用合并为一次执行,如果修改了相同的值,则会将前面的修改替换成最后一次的修改数据。

这一点在项目编码中一定要牢记,避免出现意外的逻辑 Bug。

你可能会问,这到底是什么原理呢?我曾经针对这个问题做过一场专门的技术分享直播,直播的录屏回放在这里,感兴趣可以看看:https://www.bilibili.com/video/BV19P4y1W7hX。

(B站账号:西岭老湿)

但是,对于新手来说,我建议你先收藏,因为你现在真的可能看不懂。

当然,你可能也发现了一个问题,我们说了那么多关于组件状态的问题,各种实验编码都是在 class 类组件中完成的,那函数组件中是不是和类组件一样呢?

嗯~ o( ̄▽ ̄)o……,因为 state 是类组件中的特性,而函数组件中的 state,需要具备 Hook 特性的知识作为铺垫,所以,暂时我们先留个坑,等着后面来填就行了。

截至到目前,我们已经掌握了组件的概念、组件的传值、以及组件状态的相关内容。

 

责任编辑:武晓燕 来源: 勾勾的前端世界
相关推荐

2020-06-16 14:23:28

算法网络Google

2015-09-18 11:28:23

2015-03-31 10:11:51

戴尔云计算DELL

2021-03-12 10:12:09

etState函数React

2022-06-13 06:20:42

setStatereact18

2022-08-08 17:38:45

Spring策略事务

2022-08-09 09:34:32

Spring开发

2020-02-23 18:00:18

数据泄露漏洞黑客

2017-04-12 11:15:52

ReactsetState策略

2017-01-16 17:17:23

存储超融合架构新闻

2021-06-29 09:47:34

ReactSetState机制

2022-02-25 08:54:50

setState异步React

2023-04-12 08:18:40

ChatGLM避坑微调模型

2009-05-11 14:48:11

2019-01-24 10:18:25

机器学习深度学习图像处理

2021-08-16 18:52:09

同步异步React

2020-12-16 10:00:59

Serverless数字化云原生

2022-09-26 09:53:18

开发缓存

2016-04-06 09:58:46

移动·开发技术周刊

2016-03-30 09:54:59

bug报告开发
点赞
收藏

51CTO技术栈公众号