摘要:而在第二个参数中我们输出了改变后的即第五行输出,表明我们的更改生效了。而在的回调内,我们还调用了一个定义于内的事件函数,但是该事件函数内的也是同步的形式。
在react中,setState是用以改变class组件状态的函数,它有两种用法:
一 传入一个updater函数,该函数有两个参数,一个是当前的state,还有一个是当前的props。该函数的返回值需要是一个更改的state值的对象,它将于state进行浅合并,其用法如下:
this.setState((state, props) => { return { count: state.count + props.number }; });
二 直接传入一个对象:
this.setState({ count: this.state.count + this.props.number });
setState函数还可以接受第二个参数,该参数为一个函数,将在更改的State生效之后调用:
console.log(this.state.count); // 1 this.setState({ count: 0 }, () => { console.log(this.state.count); // 0 }); console.log(this.state.count); // ? 此处即可能是1,也可能是0
从上面代码可以看到,最后一行输出的count是不固定的,这是为什么呢?
因为在react中,class内的事件处理程序会默认进行批处理,即如果你在componentDidMount里面调用三次setState函数,那么它最终会在componentDidMount执行完毕后,将三个State的更改合并为一次调用。所以这时候setState就是异步的。
而在其他场景下,setState将会是同步的,例如setTimeout内, Promise的then里面。
一个简单的例子:
class SetStateExample extends Component { constructor() { super(); this.state = { count: 0 }; this.onClick = this.onClick.bind(this); } componentDidMount() { console.log("componentDidMount before", this.state.count); this.setState({ count: this.state.count + 1 }); console.log("componentDidMount after", this.state.count); } onClick() { console.log("onClick before", this.state.count) this.setState({ count: this.state.count + 1 }, () => { console.log("setState callback", this.state.count); }); console.log("onClick after", this.state.count); Promise.resolve().then(() => { console.log("promise.then before", this.state.count); this.setState({ count: this.state.count + 1 }); console.log("promise.then after", this.state.count); this.onClassEvent(); }); } onClassEvent() { console.log("onClassEvent before", this.state.count); this.setState({ count: this.state.count + 1 }); console.log("onClassEvent after", this.state.count); } render() { return; } }count: {this.state.count}
让我们运行结果:
首先第一第二行输出是在componentDidMount里面,我们在函数内调用了setState,并在前后分别输出了改变的值,结果表明,函数调用前与函数调用后该值并没有立即改变,则表明在这里setState是一个异步调用。那么初步判定在生命周期函数内部,setState是异步的调用。
然后第三第四行输出是在onClick函数的回调里面,该函数定义在class中,通过用户点击触发。在setState调用前后我们的输出结果是一致的,这也表明其是一个异步调用。而在setState第二个参数中我们输出了改变后的count, 即第五行输出,表明我们的更改生效了。
然后第六行以后的输出是我们在onClick函数内调用了promise.resolve().then()输出的,它是一个异步调用,react是无法知道它什么时候执行,什么时候完成执行的,所以这时候react默认setState是同步的。从输出我们可以看到每次更改之后,state的值都是立即变化生效的。
而在promise的回调内,我们还调用了一个定义于class内的事件函数,但是该事件函数内的setState也是同步的形式。这说明了setState的同步或者异步与其定义位置并没有直接的关系,而应该取决于是否由React直接进行调用,因为只有是React直接调用的情况下,它才知道该函数什么时候执行完毕,才能进行批处理的优化。否则则默认是同步的调用。(具体内部实现就不展开了,因为我也不是特别懂HHHH,反正意思就大概是这么个意思)
所以当在一些回调内部调用setState时应该注意将多个setState合并,因为它是同步的,多次更新状态会很影响性能。
以及需要注意进行异步调用的时候,如果需要使用变化后的值,请确保在异步调用完成后,一般是在setState的回调内,或者在componentDidUpdate钩子内,但是请注意小心使用,因为很容易一不小心导致循环调用而崩溃。
如果你想在本应同步调用的回调内,对setState进行异步调用,即让它进行批处理,React也提供了一个API:
promise.then(() => { // Forces batching ReactDOM.unstable_batchedUpdates(() => { this.setState({a: true}); // Doesn"t re-render yet this.setState({b: true}); // Doesn"t re-render yet this.props.setParentState(); // Doesn"t re-render yet }); // When we exit unstable_batchedUpdates, re-renders once });
在unstable_batchedUpdates内部进行的setState会是异步调用,但是该API是不稳定的,因为后续的React版本更新中将会默认进行批处理即异步调用,届时该API将被删除。而这个后续的版本,很可能就是React 17
记录与分享,欢迎斧正,虚心求教
参考连接:
https://stackoverflow.com/que...
https://react.docschina.org/d...
https://github.com/Advanced-F...
https://github.com/sisterAn/b...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/106941.html
摘要:判断当前是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。将组件的暂存队列中的进行合并,获得最终要更新的对象,并将队列置为空。执行生命周期,根据返回值判断是否要继续更新。 this.setState( )方法是React.js中最常见的一种方法,利用它可以控制各种状态变化,达到页面各种交互效果,但是,我们在React开发中偶尔会发现,明明已经通过this.setState...
摘要:不保证这个状态的更新是立即执行的。这个问题导致如果开发者在之后立即去访问可能访问的不是最新的状态。不应该被直接更改,而是应该新建一个来表示更新后的状态。实验采用基于控制变量法的对照试验。至于的问题,留给读者自己吧。 React组件重新渲染的条件是: B.只要调用this.setState()就会发生重新渲染。 C.必须调用this.setState()且传递不同于当前this.setS...
摘要:我们来从设计思想上,和官方团队的回应上,了解一下否决理由。此外,还有一个方法新的接口设计支持接收一个回调函数,当其子组件挂载时,这个回调函数就会相应触发。 从 setState 那个众所周知的小秘密说起... 在 React 组件中,调用 this.setState() 是最基本的场景。这个方法描述了 state 的变化、触发了组件 re-rendering。但是,也许看似平常的 th...
摘要:我们来从设计思想上,和官方团队的回应上,了解一下否决理由。此外,还有一个方法新的接口设计支持接收一个回调函数,当其子组件挂载时,这个回调函数就会相应触发。 从 setState 那个众所周知的小秘密说起... 在 React 组件中,调用 this.setState() 是最基本的场景。这个方法描述了 state 的变化、触发了组件 re-rendering。但是,也许看似平常的 th...
摘要:异步渲染利用事件循环,延迟渲染函数的调用调用回调函数处理后跟函数的情况浅合并逻辑事件循环,关于的事件循环和的事件循环后续会单独写篇文章。 showImg(https://segmentfault.com/img/remote/1460000015785464?w=640&h=280); 看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 Rea...
阅读 3752·2021-08-11 11:16
阅读 1619·2019-08-30 15:44
阅读 1993·2019-08-29 18:45
阅读 2267·2019-08-26 18:18
阅读 993·2019-08-26 13:37
阅读 1562·2019-08-26 11:43
阅读 2108·2019-08-26 11:34
阅读 371·2019-08-26 10:59