详解React setState数据更新机制

 更新时间:2021年04月23日 09:28:57   作者:编程琐事  
这篇文章主要介绍了React setState数据更新机制的相关资料,帮助大家更好的理解和学习使用React框架,感兴趣的朋友可以了解下

为什么使用setState

在React 的开发过程中,难免会与组件的state打交道。使用过React 的都知道,想要修改state中的值,必须使用内部提供的setState 方法。为什么不能直接使用赋值的方式修改state的值呢?我们就分析一下,先看一个demo。

class Index extends React.Component {
  this.state = {
    count: 0
  }
  onClick = () => {
    this.setState({
       count: 10
    })
  }
  render() {
    return (
      <div>
        <span>{this.state.count}</span>
        <button onClick={this.onClick}>click</button>
      </div>
    )
  }
}

根据上面代码可以看到,点击按钮后把state 中 count 的值修改为 10。并更新页面的显示。所以state的改变有两个作用:对应的值改变 和 **页面更新。**要想做到这两点在react 中 非 setState 不可。 假如说我们把 onClick 的方法内容修改为 this.state.count = 10 并在方法内打印出 this.state 的值,可以看到state的值已经改变。但是页面并没有更新到最新的值。 ☆总结一下:

  • state 值的改变,目的是页面的更新,希望React 使用最新的 state来渲染页面。但是直接赋值的方式并不能让React监听到state的变化。
  • 必须通过setState 方法来告诉React state的数据已经变化。

☆扩展一下:

在vue中,采用的就是直接赋值的方式来更新data 数据,并且Vue也能够使用最新的data数据渲染页面。这是为什么呢? 在vue2中采用的是 Object.defineProperty() 方式监听数据的get 和 set 方法,做到数据变化的监听 在vue3中采用的是ES6 的 proxy 方式监听数据的变化

setState 的用法

想必所有人都会知道setState 的用法,在这里还是想记录一下: setState方法有两个参数:第一个参数可以是对象直接修改属性值,也可以是函数能够拿到上一次的state值。第二个参数是一个可选的回调函数,可以获取最新的state值 回调函数会在组件更新完成之后执行,等价于在 componentDidUpdate 生命周期内执行。

  • 第一个参数是对象时:如同上文的demo一样,直接修改state的属性值
this.setState({
	key:newState
})
  • 第一个参数是函数时:在函数内可以获取上一次state 的属性值。
// prevState 是上一次的 state,props 是此次更新被应用时的 props
this.setState((prevState, props) => {
  return {
      key: prevState.key 
  }
})

异步更新还是同步更新

setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式 将 setState() 视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。

先修改一下上面的代码,如果在onClick 方法中连续调用三次setState,根据上文可知 setState是一个异步的方式,每次调用只是将更改加入队列,同步调用的时候只会执行最后一次更新,所以结果是1而不是3。

onClick = () => {
  const { count } = this.state
  this.setState({ count: count + 1 })
  this.setState({ count: count + 1 })
  this.setState({ count: count + 1 })
}

可以把上面代码理解为 Object.assign() 方法,

Object.assign(
  state,
  { count: state.count + 1 },
  { count: state.count + 1 },
  { count: state.count + 1 }
)

如果第一个参数传入一个函数,连续调用三次,是不是和传入对象方式的结果是一样的呢?

onClick = () => {
  this.setState((prevState, props) => {
    return {
      count: prevState.count + 1
    }
  })
  this.setState((prevState, props) => {
    return {
      count: prevState.count + 1
    }
  })
  this.setState((prevState, props) => {
    return {
      count: prevState.count + 1
    }
  })
}

结果和传入对象的方式大相径庭,使用函数的方式就能够实现自增为3的效果。这又是为什么呢? 在函数内能够拿到最新的state 和 props值。由上文可知 setState 的更新是分批次的,使用函数的方式确保了当前state 是建立在上一个state 之上的,所以实现了自增3的效果。

☆总结一下: 为什么setState 方法是异步的呢?

  • 可以显著的提升性能,react16 引入了 Fiber 架构,Fiber 中对任务进行了划分和优先级的分类,优先处理优先级比较高的任务。页面的响应就是一个优先级比较高任务,所以如果setState是同步,那么更新一次就要更新一次页面,就会阻塞到页面的响应。最好的办法就是获得到多个更新,之后进行批量的更新。只更新一次页面。
  • 如果同步更新state,但是还没有执行render 函数,那么state 和 props 就不能够保持同步。

**是不是所有的setState 都是异步的形式呢?**答案是 否!!!在React 中也会存在setState 同步的场景

onClick = () => {
	this.setState({ count: this.state.count + 1 })
  console.log(this.state)
  setTimeout(() => {
    this.setState({ count: this.state.count + 1 })
    console.log(this.state)
  }, 0)
}

上面的代码会打印出**0,2。**这又是为什么呢?其实React 中的 setState 并不是严格意义上的异步函数。他是通过队列的延迟执行实现的。使用 isBatchingUpdates 判断当前的setState 是加入到更新队列还是更新页面。当 isBatchingUpdates=ture 是加入更新队列,否则执行更新。

知道了React 是使用 isBatchingUpdates 来判断是否加入更新队列。那么为什么在 setTimeout 事件中 isBatchingUpdates 值为 false ? 原因就是在React中,对HTML的原生事件做了一次封装叫做**合成事件。**所以在React自己的生命周期和合成事件中,可以控制 isBatchingUdates 的值,可以根据值来判断是否更新页面。而在宿主环境提供的原生事件中(即非合成事件),无法将 isBatchingUpdates 的值置为 false,所以就会立即执行更新。

☆所以setState 并不是有同步的场景,而是在特殊的场景下不受React 的控制 **

总结

setState 并不是单纯的同步函数或者异步函数,他的同步和异步的表现差异体现在调用的场景不同。在React 的生命周期和合成事件中他表现为异步函数。而在DOM的原生事件等非合成事件中表现为同步函数。

以上就是详解React setState数据更新机制的详细内容,更多关于React setState数据更新机制的资料请关注脚本之家其它相关文章!

相关文章

  • React中完整实例讲解Recoil状态管理库的使用

    React中完整实例讲解Recoil状态管理库的使用

    这篇文章主要介绍了React中Recoil状态管理库的使用,Recoil的产生源于Facebook内部一个可视化数据分析相关的应用,在使用React的实现的过程中,因为现有状态管理工具不能很好的满足应用的需求,因此催生出了Recoil,对Recoil感兴趣可以参考下文
    2023-05-05
  • React学习之JSX与react事件实例分析

    React学习之JSX与react事件实例分析

    这篇文章主要介绍了React学习之JSX与react事件,结合实例形式分析了React中JSX表达式、属性、嵌套与react事件相关使用技巧,需要的朋友可以参考下
    2020-01-01
  • React路由拦截模式及withRouter示例详解

    React路由拦截模式及withRouter示例详解

    这篇文章主要为大家介绍了React路由拦截模式及withRouter示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 关于antd tree和父子组件之间的传值问题(react 总结)

    关于antd tree和父子组件之间的传值问题(react 总结)

    这篇文章主要介绍了关于antd tree 和父子组件之间的传值问题,是小编给大家总结的一些react知识点,本文通过一个项目需求实例代码详解给大家介绍的非常详细,需要的朋友可以参考下
    2021-06-06
  • React和Vue组件更新的实现及区别

    React和Vue组件更新的实现及区别

    React 和 Vue 都是当今最流行的前端框架,它们都实现了组件化开发模式,本文将从React和Vue的组件更新原理入手,剖析两者虚拟DOM difer算法的异同点,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • React中使用axios发送请求的几种常用方法

    React中使用axios发送请求的几种常用方法

    本文主要介绍了React中使用axios发送请求的几种常用方法,主要介绍了get和post请求,具有一定的参考价值,感兴趣的可以了解一下
    2021-08-08
  • React onClick/onChange传参(bind绑定)问题

    React onClick/onChange传参(bind绑定)问题

    这篇文章主要介绍了React onClick/onChange传参(bind绑定)问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • React useEffect的理解与使用

    React useEffect的理解与使用

    useEffect是react v16.8新引入的特性。我们可以把useEffect hook看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三个函数的组合
    2022-12-12
  • React-hook-form-mui基本使用教程(入门篇)

    React-hook-form-mui基本使用教程(入门篇)

    react-hook-form-mui可以帮助开发人员更轻松地构建表单,它结合了React Hook Form和Material-UI组件库,使用react-hook-form-mui,开发人员可以更快速地构建表单,并且可以轻松地进行表单验证和数据处理,本文介绍React-hook-form-mui基本使用,感兴趣的朋友一起看看吧
    2024-02-02
  • 浅谈React useDebounce 防抖原理

    浅谈React useDebounce 防抖原理

    本文主要介绍了浅谈React useDebounce 防抖原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08

最新评论