在 React 平常的使用中,一个很重要的点就是,不要直接去修改 state。例如:this.state.count = 1
是没法触发 React 去更新视图的。由于React的机制规定,一个state的更新,首先须要调用 setState 方法。javascript
this.setState({
count: 1
})
复制代码
这样便能触发从新渲染。稍有经验的开发者会知道,setState 方法实际上是 “异步” 的。即立马执行以后,是没法直接获取到最新的 state 的,须要通过 React 对 state 的全部改变进行合并处理以后,才会去计算新的虚拟dom,再根据最新的虚拟dom去从新渲染真实dom。java
class App extends Component {
state = {
count: 0
}
componentDidMount(){
this.setState({count: this.state.count + 1})
console.log(this.state.count) // 0
}
render(){
...
}
}
复制代码
demo请点击react
那怎么才能获取到修改后的state呢?React为咱们提供了一个回调去实现。git
...
this.setState({count: this.state.count + 1}, ()=>{
console.log(this.state.count) // 1
})
...
复制代码
回调里的 state 即是最新的了,缘由是该回调的执行时机在于state合并处理以后。若是咱们这样去作:github
...
this.setState({count: this.state.count + 1})
this.setState({count: this.state.count + 1})
...
复制代码
实际最终的 count 会等于 1,缘由是执行时获得的 this.state.count = 0
。那怎么实现结果为 2 呢?segmentfault
...
this.setState(prevState => {count: prevState.count + 1});
this.setState(prevState => {count: prevState.count + 1});
...
复制代码
setState()
实际上能够接受一个函数做为参数,函数的首个参数就是上一次的state。后端
以上介绍了setState
的三种使用方式,下面咱们来看看它们的执行时机是怎样的:性能优化
...
this.setState({ count: this.state.count + 1 });
console.log("console: " + this.state.count); // 0
this.setState({ count: this.state.count + 1 }, () => {
console.log("console from callback: " + this.state.count); // 2
});
this.setState(prevState => {
console.log("console from func: " + prevState.count); // 1
return {
count: prevState.count + 1
};
}, ()=>{
console.log('last console: '+ this.state.count)
});
...
复制代码
执行结果:bash
console: 0
console from func: 1
console from callback: 2
last console: 2
复制代码
React 其实会维护着一个 state 的更新队列,每次调用 setState 都会先把当前修改的 state 推动这个队列,在最后,React 会对这个队列进行合并处理,而后去执行回调。根据最终的合并结果再去走下面的流程(更新虚拟dom,触发渲染)。微信
由于setState()
以后没法立马获取最新的 state,给人的感受即是异步去设置状态。也确实是有异步的感受(实 际原理后面讲诉)。那么为何 React 要把状态的更新设计成这种方式呢?直接 this.state.count = 1
很差吗?
有兴趣的能够点击看看:github.com/facebook/re…
这边简单总结下:
state
是同步更新,props
也不是。(你只有在父组件从新渲染时才能知道props
)state
的更新延缓到最后批量合并再去渲染对于应用的性能优化是有极大好处的,若是每次的状态改变都去从新渲染真实dom,那么它将带来巨大的性能消耗。咱们先来看一段代码,执行前建议你们先预估下结果:
class App extends Component {
state = {
count: 0
};
componentDidMount() {
// 生命周期中调用
this.setState({ count: this.state.count + 1 });
console.log("lifecycle: " + this.state.count);
setTimeout(() => {
// setTimeout中调用
this.setState({ count: this.state.count + 1 });
console.log("setTimeout: " + this.state.count);
}, 0);
document.getElementById("div2").addEventListener("click", this.increment2);
}
increment = () => {
// 合成事件中调用
this.setState({ count: this.state.count + 1 });
console.log("react event: " + this.state.count);
};
increment2 = () => {
// 原生事件中调用
this.setState({ count: this.state.count + 1 });
console.log("dom event: " + this.state.count);
};
render() {
return (
<div className="App"> <h2>couont: {this.state.count}</h2> <div id="div1" onClick={this.increment}> click me and count+1 </div> <div id="div2">click me and count+1</div> </div>
);
}
}
复制代码
探讨前,咱们先简单了解下react的事件机制:react为了解决跨平台,兼容性问题,本身封装了一套事件机制,代理了原生的事件,像在jsx
中常见的onClick
、onChange
这些都是合成事件。
那么以上4种方式调用setState()
,后面紧接着去取最新的state,按以前讲的异步原理,应该是取不到的。然而,setTimeout
中调用以及原生事件中调用的话,是能够立马获取到最新的state的。根本缘由在于,setState并非真正意义上的异步操做,它只是模拟了异步的行为。React中会去维护一个标识(isBatchingUpdates
),判断是直接更新仍是先暂存state进队列。setTimeout
以及原生事件都会直接去更新state,所以能够当即获得最新state。而合成事件和React生命周期函数中,是受React控制的,其会将isBatchingUpdates
设置为 true
,从而走的是相似异步的那一套。
此处总结是直接引用了:juejin.im/post/5b45c5…
setState
只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout
中都是同步的。setState
的“异步”并非说内部由异步代码实现,其实自己执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新以前,致使在合成事件和钩子函数中无法立马拿到更新后的值,形式了所谓的“异步”,固然能够经过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。setState
的批量更新优化也是创建在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中若是对同一个值进行屡次 setState
, setState
的批量更新策略会对其进行覆盖,取最后一次的执行,若是是同时 setState
多个不一样的值,在更新时会对其进行合并批量更新。参考:
坦白说,本身的水平仍是挺有限的,还须要努力努力再努力。只有懂了不少,才能写出有意义的东西吧。如今写的,大多都是根据别人的文章,本身理解了以后,换成本身的语言再表达一遍而已。。
慢慢来,总有一天能够突破这个界限的,真正写出属于本身的东西。
另外,真心感谢这些文章的做者给予的帮助。
微信号:rcgrcg,欢迎交友~
为了生计,我也接外包项目的哦~
网站搭建(PC、H五、先后端全包,咱们有团队的哦),APP开发(安卓和IOS),都是有成功案例的哦。
有兴趣的请联系我!!服务包您满意的那种!!