ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

聊聊React类组件中的setState()的同步异步(附面试题)

2022-02-08 20:30:52  阅读:149  来源: 互联网

标签:count 面试题 console log 更新 React state setState


一.setState()更新状态的两种写法

  1. setState(updater, [callback]),
    updater为返回stateChange对象的函数: (state, props) => stateChange
    接收的state和props被保证为最新的
  2. setState(stateChange, [callback])
    stateChange为对象,
    callback是可选的回调函数, 在状态更新且界面更新后才执行
  3. 总结:
    对象方式是函数方式的简写方式
    如果新状态不依赖于原状态 ===> 使用对象方式
    如果新状态依赖于原状态 ===> 使用函数方式
    如果需要在setState()后获取最新的状态数据, 在第二个callback函数中读取
  4. 例子:
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>01_setState()的使用</title>
</head>
<body>

<div id="example"></div>

<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script src="https://cdn.bootcss.com/remarkable/1.7.1/remarkable.min.js"></script>

<script type="text/babel">

  class A extends React.Component {

    state = {
      count: 1
    }

    test1 = () => {
      this.setState(state => ({count: state.count + 1}))
      console.log('test1 setState()之后', this.state.count)
    }

    test2 = () => {
      /*const count = this.state.count + 1
      this.setState({
        count
      })*/
      this.setState({
        count: 3
      })
      console.log('test2 setState()之后', this.state.count)
    }

    test3 = () => {
      this.setState(state => ({count: state.count + 1}), () => { // 在状态更新且界面更新之后回调
        console.log('test3 setState callback()', this.state.count)
      })
    }

    render() {
      console.log('A render()')
      return (
        <div>
          <h1>A组件: {this.state.count}</h1>
          <button onClick={this.test1}>A 测试1</button>&nbsp;&nbsp;
          <button onClick={this.test2}>A 测试2</button>&nbsp;&nbsp;
          <button onClick={this.test3}>A 测试3</button>&nbsp;&nbsp;
        </div>
      )
    }
  }

  ReactDOM.render(<A/>, document.getElementById('example'))
</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当我们依次按下1、2、3按钮,我们会发现1按钮的事件监听函数运行时是先运行console.log('test1 setState()之后', this.state.count)这句代码然后在进行的render(),而在代码中this.setState(state => ({count: state.count + 1}))这句是在前的,由此我们可以推测setState()是异步的 ,同理2,3按钮也是。
值得一提的是,按钮3中this.setState(state => ({count: state.count + 1}), () => { // 在状态更新且界面更新之后回调 console.log('test3 setState callback()', this.state.count) }) 中有一个回调函数,在我们一般情况下是用其简写形式(对象形式),只有在需要setState()后获取最新的状态数据时才会用到函数形式的setState()。

二.setState()更新状态是异步还是同步的?

  1. 执行setState()的位置?
    在react控制的回调函数中: 生命周期勾子 / react事件监听回调
    非react控制的异步回调函数中: 定时器回调 / 原生事件监听回调 / promise回调 /…
  2. 异步 OR 同步?
    react相关回调中: 异步
    其它异步回调中: 同步
  3. 例子
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>02_setState()异步OR同步更新</title>
</head>
<body>

<div id="example"></div>

<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>

<script type="text/babel">
  class StateTest extends React.Component {

    state = {
      count: 0,
    }

    /*
     react事件监听回调中, setState()是异步更新状态
     */
    update1 = () => {
      console.log('update1 setState()之前', this.state.count)
      this.setState(state => ({count: state.count + 1}))
      console.log('update1 setState()之后', this.state.count)
    }

    /*
     react生命周期勾子中, setState()是异步更新状态
     */
    componentDidMount () {
      console.log('componentDidMount setState()之前', this.state.count)
      this.setState(state => ({count: state.count + 1}))
      console.log('componentDidMount setState()之后', this.state.count)
    }

    /*
    定时器回调 / 原生事件监听回调 / promise回调 /...
     */
    update2 = () => {
      setTimeout(() => {
        console.log('setTimeout setState()之前', this.state.count)
        this.setState(state => ({count: state.count + 1}))
        console.log('setTimeout setState()之后', this.state.count)
      })
    }
    update3 = () => {
      const h2 = this.refs.count
      h2.onclick = () => {
        console.log('onclick setState()之前', this.state.count)
        this.setState(state => ({count: state.count + 1}))
        console.log('onclick setState()之后', this.state.count)
      }
    }
    update4 = () => {
      Promise.resolve().then(value => {
        console.log('Promise setState()之前', this.state.count)
        this.setState(state => ({count: state.count + 1}))
        console.log('Promise setState()之后', this.state.count)
      })
    }


    update5 = () => {
      console.log('onclick setState()之前', this.state.count)
      this.setState(state => ({count: state.count + 1}))
      console.log('onclick setState()之后', this.state.count)
      console.log('onclick setState()之前2', this.state.count)
      this.setState(state => ({count: state.count + 1}))
      console.log('onclick setState()之后2', this.state.count)
    }

    update6 = () => {
      console.log('onclick setState()之前', this.state.count)
      this.setState({count: this.state.count + 1})
      console.log('onclick setState()之后', this.state.count)
      console.log('onclick setState()之前2', this.state.count)
      this.setState({count: this.state.count + 1})
      console.log('onclick setState()之后2', this.state.count)
    }

    update7 = () => {
      console.log('onclick setState()之前', this.state.count)
      this.setState({count: this.state.count + 1})
      console.log('onclick setState()之后', this.state.count)

      console.log('onclick setState()之前2', this.state.count)
      this.setState(state => ({count: state.count + 1}))
      console.log('onclick setState()之后2', this.state.count)
    }


    render() {
      const {count} = this.state
      console.log('render()', count)
      return (
        <div>
          <h2 ref='count'>{count}</h2>
          <button onClick={this.update1}>更新1</button> ---
          <button onClick={this.update2}>更新2</button> &nbsp;
          <button onClick={this.update3}>更新3</button> &nbsp;
          <button onClick={this.update4}>更新4</button> ---
          <button onClick={this.update5}>更新5</button> &nbsp;
          <button onClick={this.update6}>更新6</button> &nbsp;
          <button onClick={this.update7}>更新7</button> &nbsp;
        </div>
      )
    }
  }

  ReactDOM.render(<StateTest/>, document.getElementById('example')) // 渲染组件标签, 内部会调用组件标签对象的render()虚拟DOM

</script>
</body>
</html>

更新1
更新2
更新3
更新4
更新5
更新6
更新7
结论我已经放在开头了,但是在更新6的时候我们发现 我们明明代码里写了两次setState(),而最终结果却只更新了一次,而更新5同样是写了两次setState()却是两次作用后的结果,这是为什么呢?

三.关于异步的setState()

  1. 多次调用, 如何处理?
    setState({}): 合并更新一次状态, 只调用一次render()更新界面 —状态更新和界面更新都合并了
    setState(fn): 更新多次状态, 但只调用一次render()更新界面 —状态更新没有合并, 但界面更新合并了

  2. 如何得到异步更新后的状态数据?
    在setState()的callback回调函数中

四.面试题

注释里箭头 左侧为次序,右侧为打印出的值

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>03_setState()面试题</title>
</head>
<body>

<div id="example"></div>

<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>

<script type="text/babel">
  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>
      )
    }
  }

  ReactDOM.render(<StateTest/>, document.getElementById('example'))

</script>
</body>
</html>


标签:count,面试题,console,log,更新,React,state,setState
来源: https://blog.csdn.net/nxcniuxiangchen/article/details/122830023

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有