state
state的存在是为了动态改变组件,比如根据不同的用户操作和网络请求,来重新渲染组件。
setState()是React给我们的一个API,用来改变或定义state。
setState()的批量操作(batching)
在一个事件handler函数中,不管setState()被调用多少次,他们也会在函数执行结束以后,被归结为一次重新渲染, 可以优化性能, 这个等到最后一起执行的行为被称为batching。
所以在函数内的setState()是有序的,如果要更改同一个state key,最后被调用的总会覆盖之前的。
因为batching的存在,所以这样的代码会和期待有出入。
//假设现在this.state.value = 0;
function eventHandler(){
this.setState({value:this.state.value + 1});
this.setState({value:this.state.value + 1});
this.setState({value:this.state.value + 1});
}
//最后this.state.value仍然会是1,不是3;
所以不能依赖this.state来计算未来状态。如果想实现这样的效果,应该传一个函数给setState。这个函数有两个参数,第一个为previous state,第二个为props。这里的例子和props无关,只需要第一个参数,所以要达到效果,代码是这样
// 假设 this.state = { value: 0 };
function eventHandler(){
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
}
//现在this.state.value === 3;
到这里我们得到结论,setState是异步执行的。
如React文档所说:
所以当更新state,然后想打印state的时候,应该使用回调。
this.setState({key: val},()=>console.log(this.state));
所以setState总是异步的,吗?
当setState()不在事件Handler函数中,如在使用ajax的时候,这种batching的异步表现又不会发生。
promise.then(() => {
// 不在事件函数中,所以setState立刻执行
this.setState({a: true}); // 重新渲染 {a: true, b: false }
this.setState({b: true}); // 重新渲染 {a: true, b: true }
});
同步异步要分情况来看:
1. React事件函数使用,像这样用最常用的形式绑定函数
constructor(props){
...
this.onClick = this.onClick.bind(this);
}
onClick(){
this.setState({key:val});
}
render(){
return(
<div>
<button onClick = {this.onClick}>Click me</button>
</div>
}
这里batching发生,异步表现,是因为这种常规情景下React “知道”什么时候退出该事件,什么时候进行。原理可以参考这篇很简洁易懂的文章
2.其他情景,如上面的ajax情景,或这样用addEventListener绑定函数
componentDidMount(){
document.querySelector('#btn').addEventListener('click,this.onClick);
}
render(){
return(
<div>
<button id="btn">Click me</button>
</div>
}
}
脱离了React的控制,React不知道如何进行Batch Update,setState()就会是同步的。