博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
setState 同步更新
阅读量:6353 次
发布时间:2019-06-22

本文共 3049 字,大约阅读时间需要 10 分钟。

hot3.png

 

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 (      
My IP is {this.state.ipAddress || 'Unknown'}
); }}

传入状态计算函数

除了使用回调函数的方式监听状态更新结果之外,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 
{this.state.count}
}}

直观的写法我们可以连续调用两次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://my.oschina.net/sunzitong/blog/1544738

你可能感兴趣的文章
c# GC 新典型
查看>>
ssh bash 通配符
查看>>
seajs在jquery多个版本下引用jquery的插件的方案
查看>>
关于网络上java,php和.net的“口角之争“的一点想法 !
查看>>
python 第二周(第十三天) 我的python成长记 一个月搞定python数据挖掘!(21) -正则表达式re...
查看>>
[POI2011]SEJ-Strongbox
查看>>
20文件
查看>>
Android开发Intent应用概述
查看>>
【Go】并发编程
查看>>
VMware虚拟化NSX-Manager命令行更改admin用户密码
查看>>
悦纳自己
查看>>
python字符串函数
查看>>
ORM框架Hibernate (四)MyEclipse Hibernate Tool 逆向生成实体类
查看>>
去掉iphone连接电脑时会出现的弹出窗口
查看>>
【python】-- web开发之HTML
查看>>
vs2015 去除 git 源代码 绑定
查看>>
解决firefox的button按钮文字不能垂直居中
查看>>
网络协议端口号详解
查看>>
大话数据结构读后感——第一章
查看>>
各种排序
查看>>