在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的值已经改变。但是页面并没有更新到最新的值。 总结一下:
扩展一下:
在vue中,采用的就是直接赋值的方式来更新data 数据,并且Vue也能够使用最新的data数据渲染页面。这是为什么呢? 在vue2中采用的是 Object.defineProperty() 方式监听数据的get 和 set 方法,做到数据变化的监听 在vue3中采用的是ES6 的 proxy 方式监听数据的变化
想必所有人都会知道setState 的用法,在这里还是想记录一下: setState方法有两个参数:第一个参数可以是对象直接修改属性值,也可以是函数能够拿到上一次的state值。第二个参数是一个可选的回调函数,可以获取最新的state值 回调函数会在组件更新完成之后执行,等价于在 componentDidUpdate
生命周期内执行。
this .setState({ key:newState }) |
// 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 方法是异步的呢?
**是不是所有的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的原生事件等非合成事件中表现为同步函数。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
长按识别二维码并关注微信
更方便到期提醒、手机管理