7.19 深度剖析react中setState

本文详细探讨了React中setState的异步特性和同步情况,包括在生命周期钩子和非React事件中的行为。解释了解决setState异步问题的方法,如通过回调函数获取最新值。同时,文章指出了setState在更新引用类型时的渲染问题,并给出了正确的处理方式。最后,通过一道面试题展示了setState在不同场景下如何影响组件的渲染和状态更新顺序。
摘要由CSDN通过智能技术生成

函数法

this.setState(state => ({
  roles: [...state.roles,role]
}))

能保证每次接收到的state最新支持同一个函数多次setState同一个变量

对象法

this.setState({
   roles: [...this.state.roles,role]
})

多次setState同一个变量,不会产生多次效果,(只能产生一次效果)

总结:一般来说,当我们更新的变量与原变量无关时,使用对象法,否则使用函数法

setState是同步还是异步

  1. react控制的回调函数中是异步的,例如
    生命周期钩子/react监听事件
//生命周期钩子
componentDidMount() {

}
//react监听事件
getList = () => {

}
  1. 非react控制的回调函数中是同步的,例如
    定时器回调/ 原生事件监听/promise回调
//定时器回调
setTimeout(() => {},1000)
// 原生事件监听
btn.onclick = function() {}
//promise回调
new Promise((resolve,reject) => {})

解决setState的异步问题

//可以通过setState的第二个参数(回调函数),拿到最新的值
getList = () => {
   this.setState({
     role
   }, () => {
   console.log('最新的role',role);
})
}

setState的render问题

如果setState传递的是一个引用类型,只改变对象(数组)中的某个值,不会render

//只改变对象中的某个属性,不能够更新成功
const m1 = this.state.m1;
m1.count = 2;
this.setState({m1})

正确做法

this.setState(state => ({
  m1:{count:state.m1.count+1}  
  或者 
  const m1 = this.state.m1;
  m1.count = 2;
  m1: {...m1}
}))

关于setState的面试题

 <div id="example"></div>
    class StateTest extends React.Component {

      state = {
        count: 0,
      }

      componentDidMount() {
        this.setState({ count: this.state.count + 1 })
        this.setState({ count: this.state.count + 1 })
        console.log(this.state.count) // 2 ==> 0

        this.setState(state => ({ count: state.count + 1 }))
        this.setState(state => ({ count: state.count + 1 }))
        console.log(this.state.count) // 3 ==> 0

        setTimeout(() => {
          this.setState({ count: this.state.count + 1 })
          console.log('timeout', this.state.count) // 10 ==> 6

          this.setState({ count: this.state.count + 1 })
          console.log('timeout', this.state.count) // 12 ==> 7
        }, 0)

        Promise.resolve().then(value => {
          this.setState({ count: this.state.count + 1 })
          console.log('promise', this.state.count)  // 6 ==>4

          this.setState({ count: this.state.count + 1 })
          console.log('promise', this.state.count) // 8 ==> 5
        })
      }

      render() {
        const count = this.state.count
        console.log('render', count)  // 1 ==> 0   4 ==>3   5 ==>4  7 ==>5  9 ==>6  11 ==>7
        return (
          <div>
            <p>{count}</p>
          </div>
        )
      }
    }

前一个数字代表顺序,后一个数字代表打印的结果

1 ==> 0 程序第一次进来,首先render,读取state的值为0
2 ==> 0 由于在钩子函数中,setState是异步函数,(promise和setTimeout回调函数中是同步的)所以先打印,结果还是0
3 ==> 0 原因与2相同
4 ==>3 由于setState和promise是微任务,setTimeout是宏任务,先执行宏任务,在执行微任务,所以先执行setState =》promise(按顺序)=》setTimeout
经过4次setState,但是this.setState({ count: this.state.count + 1 })这种方式,更新变量效果会合并,相当于只执行一次(异步setState,执行完所有的setState,才会render,而同步的setState,每次都会触发render)
5 ==> 4 执行promise中的setState导致render,

6==>4 promise中打印count

7==> 5 因为是同步的所以结果不会合并
8==>5 之后但原因都和之前的相同,这里就不雷同了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值