天天看点

7.19 深度剖析react中setState函数法对象法setState是同步还是异步解决setState的异步问题setState的render问题关于setState的面试题

setState

  • 函数法
  • 对象法
  • setState是同步还是异步
  • 解决setState的异步问题
  • setState的render问题
  • 关于setState的面试题

函数法

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 之后但原因都和之前的相同,这里就不雷同了

继续阅读