React中setState同步更新策略
setState 同步更新
我们在上文中提及,为了提高性能React将setState设置为批次更新,即是异步操作函数,并不能以顺序控制流的方式设置某些事件,我们也不能依赖于this.state来计算未来状态。典型的譬如我们希望在从服务端抓取数据并且渲染到界面之后,再隐藏加载进度条或者外部加载提示:
componentDidMount() {
fetch('https://example.com')
.then((res) => res.json())
.then(
(something) => {
this.setState({ something });
StatusBar.setNetworkActivityIndicatorVisible(false);
}
);
}
因为setState函数并不会阻塞等待状态更新完毕,因此setNetworkActivityIndicatorVisible有可能先于数据渲染完毕就执行。我们可以选择在componentWillUpdate与componentDidUpdate这两个生命周期的回调函数中执行setNetworkActivityIndicatorVisible,但是会让代码变得破碎,可读性也不好。实际上在项目开发中我们更频繁遇见此类问题的场景是以某个变量控制元素可见性:
this.setState({showForm : !this.state.showForm});
我们预期的效果是每次事件触发后改变表单的可见性,但是在大型应用程序中如果事件的触发速度快于setState的更新速度,那么我们的值计算完全就是错的。本节就是讨论两种方式来保证setState的同步更新。
完成回调
setState函数的第二个参数允许传入回调函数,在状态更新完毕后进行调用,譬如:
this.setState({
load: !this.state.load,
count: this.state.count + 1
}, () => {
console.log(this.state.count);
console.log('加载完成')
});
这里的回调函数用法相信大家很熟悉,就是JavaScript异步编程相关知识,我们可以引入Promise来封装setState:
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
setStateAsync 返回的是Promise对象,在调用时我们可以使用Async/Await语法来优化代码风格:
async componentDidMount() {
StatusBar.setNetworkActivityIndicatorVisible(true)
const res = await fetch('https://api.ipify.org?format=json')
const {ip} = await res.json()
await this.setStateAsync({ipAddress: ip})
StatusBar.setNetworkActivityIndicatorVisible(false)
}
这里我们就可以保证在setState渲染完毕之后调用外部状态栏将网络请求状态修改为已结束,整个组件的完整定义为:
class AwesomeProject extends Component {
state = {}
setStateAsync(state) {
...
}
async componentDidMount() {
...
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
My IP is {this.state.ipAddress || 'Unknown'}
</Text>
</View>
);
}
}
传入状态计算函数
除了使用回调函数的方式监听状态更新结果之外,React还允许我们传入某个状态计算函数而不是对象来作为第一个参数。状态计算函数能够为我们提供可信赖的组件的State与Props值,即会自动地将我们的状态更新操作添加到队列中并等待前面的更新完毕后传入最新的状态值:
this.setState(function(prevState, props){
return {showForm: !prevState.showForm}
});
这里我们以简单的计数器为例,我们希望用户点击按钮之后将计数值连加两次,基本的组件为:
class Counter extends React.Component{
constructor(props){
super(props);
this.state = {count : 0}
this.incrementCount = this.incrementCount.bind(this)
}
incrementCount(){
...
}
render(){
return <div>
<button onClick={this.incrementCount}>Increment</button>
<div>{this.state.count}</div>
</div>
}
}
直观的写法我们可以连续调用两次setState函数,这边的用法可能看起来有点怪异,不过更多的是为了说明异步更新带来的数据不可预测问题。
incrementCount(){
this.setState({count : this.state.count + 1})
this.setState({count : this.state.count + 1})
}
上述代码的效果是每次点击之后计数值只会加1,实际上第二个setState并没有等待第一个setState执行完毕就开始执行了,因此其依赖的当前计数值完全是错的。我们当然可以使用上文提及的setStateAsync来进行同步控制,不过这里我们使用状态计算函数来保证同步性:
incrementCount(){
this.setState((prevState, props) => ({
count: prevState.count + 1
}));
this.setState((prevState, props) => ({
count: prevState.count + 1
}));
}
这里的第二个setState传入的prevState值就是第一个setState执行完毕之后的计数值,也顺利保证了连续自增两次。
转载https://zhuanlan.zhihu.com/p/24781259?refer=wxyyxc1992
React中setState同步更新策略的更多相关文章
- React中setState学习总结
react中setState方法到底是异步还是同步,其实这个是分在什么条件下是异步或者同步. 1.先来回顾一下react组件中改变state的几种方式: import React, { Compone ...
- React中setState 什么时候是同步的,什么时候是异步的?
class Example extends React.Component { constructor() { super(); this.state = { val: 0 }; } componen ...
- react中setState用法
setState()更新状态的2种写法 setState(updater, [callback]), updater为返回stateChange对象的函数: (state, props) => ...
- React中setState的怪异行为 ——setState没有即时生效
setState可以说是React中使用频率最高的一个函数了,我们都知道,React是通过管理状态来实现对组件的管理的,当this.setState()被调用的时候,React会重新调用render方 ...
- React中setState如何修改深层对象?
在React中经常会使用到setState,因为在react生态中,state就是一切.在开发过程中,时长会在state中遇到一些比较复杂的数据结构,类似下面这样的: 这时需要我们修改list中obj ...
- React中setState方法说明
setState跟新数据是同步还是异步? setState跟新数据是异步的. 如何用代码表现出来是异步的. 点击按钮更新数据,然后去打印这个值看一下 setState跟新数据是异步的 class Fa ...
- react中PureComponent浅对比策略
PureComponent实现了Component中没有实现的shouComponentUpdata()方法,会对state和props进行一次浅对比,本文介绍一下浅对比策略 源码中,实现浅对比的函数 ...
- 解决idea spring boot项目中target中没有同步更新最新目录文件及资源
idea不像eclipse那样自动将新保存的文件或目录及其他资源更新到target目录中,必须在pom.xml中设置 <build> <resources> <resou ...
- tensorflow中常用学习率更新策略
神经网络训练过程中,根据每batch训练数据前向传播的结果,计算损失函数,再由损失函数根据梯度下降法更新每一个网络参数,在参数更新过程中使用到一个学习率(learning rate),用来定义每次参数 ...
随机推荐
- JavaScript实现StringBuffer
function StringBuffer() { this._strings = new Array(); } StringBuffer.prototype.Append = function(_s ...
- vs.net打包(自动检测环境并安装.net framwork)
vs.net打包程序或者制作安装程序时自动检测环境并安装.netframwork的设置方法之前我看过文档也做过,但是过一段时间又忘了,现在终于又找到方法了,还是把这个方法写下来吧,方便自己也方便大家将 ...
- 写C#代码时用到的中文简体字 、繁体字 对应的转化 (收藏吧)
简体字 下面有与之对应的繁体字 private const String Jian = "啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊澳芭捌扒叭吧笆疤巴拔跋靶 ...
- Datatable paging,Repeater with Paging
/// <summary> /// 塗聚文 /// 20140225 /// </summary> public partial class DatatablePage : S ...
- 超级表格:要山寨Excel,还是与之Say Byebye?
创业产品难免被人拿来与现有的知名产品比较,创业者也喜欢把自己的产品与现有的知名产品比较. 我,超级表格创始人,对此有话说. 当我要在各种场合描述超级表格是什么时,也纠结过. 向用户描述时,说超级表格类 ...
- Windows 系统 Unicode 文件名操作(新建、重命名、枚举、复制)全攻略
常见的那些文件操作函数都不支持,于是为了达到目的,需要各种方法配合,应该是不如其他语言方便.我只是想看看Perl到底是否适合做这件事,于是折腾了一回. 文件的建立: 模块:Win32 Code: [全 ...
- SAP Fiori里两种锁机制(lock)的实现
方法1: ETAG机制 SAP CRM Fiori采用了这种机制. 看一个具体的例子来理解.假设我用用户名Jerry选中了这个ID为3456的Opportunity,点击Edit按钮之后: 会触发一个 ...
- pymongo 常用操作函数
pymongo 是 mongodb 的 python Driver Editor. 记录下学习过程中感觉以后会常用多一些部分,以做参考. 1. 连接数据库 要使用pymongo最先应该做的事就是先连上 ...
- python:线程进阶
1,守护线程 import time from threading import Thread def func(): print('开始执行子线程') time.sleep(3) print('子线 ...
- python:正则模块
1,正则表达式 正则表达式是用来做字符串的匹配的,正则有他自己的规则,和python没有关系,一种匹配字符串的规则. 2,字符组 在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表 ...