常规情况
在同一个方法中多次setState是会被合并的,并且对相同属性的设置只保留最后一次的设置;
import React from 'react';
export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentWillMount() {
let me = this;
me.setState({
count: me.state.count + 2
});
me.setState({
count: me.state.count + 1
});
}
componentDidMount() {
let me = this;
me.setState({
count: me.state.count + 2
});
me.setState({
count: me.state.count + 1
});
}
onClick() {
let me = this;
me.setState({
count: me.state.count + 1
});
me.setState({
count: me.state.count + 1
});
}
render() {
console.log(this.state.count);
console.log('1111111111111111111111111111111111111111111');
return (
<div>
<h1>{this.state.count}</h1>
<input type="button" value="点击我" onClick={this.onClick.bind(this)} /><br />
<br />
</div>
)
}
}
上述执行过程如下:
willmount
中的setState
会合并成一次执行,count
只会保留最后一次的设置,前面的放弃,所以willmount
之后是1
,并不是3
;并且在render
之前执行,不会引起新的render
- render之后执行didMount,setState做同样的处理,这是
count
是2
,并且引起新的render- 点击按钮,
setState
做同样处理,count
是3
,引起新的render
定时器中的setState
定时器中的setState,每次都会引起新的render,即使是同一个定时器中的多次setState
代码更改成如下:
componentWillMount() {
let me = this;
setTimeout(() => {
me.setState({
count: me.state.count + 1
});
me.setState({
count: me.state.count + 1
});
}, 0);
}
componentDidMount() {
let me = this;
setTimeout(() => {
me.setState({
count: me.state.count + 1
});
me.setState({
count: me.state.count + 1
});
}, 0);
}
onClickTime() {
let me = this;
setTimeout(() => {
me.setState({
count: me.state.count + 1
});
me.setState({
count: me.state.count + 1
});
}, 0);
}
上述代码,每次
setState
都会引发新的render,需要深入了解的可以查查
setState
的原理,简单理解是定时器中的
setState
没走
react
的事物机制,执行时批量更新没被设置
true
,所以每次都直接render了。
原生事件中的setState
在按钮原生事件中定义的
setState
,和定时器效果一样,
每次setState
都会引起新的render
react事件是合并的成一次render的。
componentDidMount() {
this.button.addEventListener('click', this.onClick.bind(this, '原生事件'), false);
}
onClick(info) {
console.log(info);
this.setState({
count: ++count
});
this.setState({
count: ++count
});
}
render() {
console.log(this.state.count);
return <div>
<input type="button" ref={input => this.button = input} onClick={this.onClick.bind(this, 'React事件')} value="生成计时器" />
<div>Count:{this.state.count}</div>
</div>
}
点击按钮,先执行原生事件,再执行react事件,但是原生事件会触发两次render,react事件触发一次。
总结
上述是我对setState的理解,抛砖引玉,希望帮助大家有方向的去了解react原理机制。刚开始接触,很多同学想深入了解,但可能不知道从何入手,这也是我遇到过的困扰,所以现在分享出来,希望能帮助大家少走弯路,更快的、更有准针对性的去研究学习React。
以下为补充内容
回调不会触发react的批量更新机制
其实在回调函数中,setState是不会触发批量更新机制的,无论是promise,ajax,setTimeout回调等等,同时设置多次setState,每个setState都会单独执行并render,因为上下文发生了变化。
下面是验证code
onClickBtn = () => {
// const promise = new Promise((resolve, reject) => {
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// resolve();
// });
// promise.then(() => {
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// });
fetch("/api/getlist")
.then(response => {
return response.json();
})
.then(data => {
console.log(JSON.stringify(data));
this.setState({ count: this.state.count + 1 });
console.log(this.state.count);
this.setState({ count: this.state.count + 1 });
console.log(this.state.count);
});
};
生命周期和事件中多次setState的区别
在写demo时发现,虽然didMount
中的多次setState
会被合并,符合正常的规律,但是通过调试发现,在didMount
中isBatchingUpdates
始终是false
,而事件调用触发的setState
,isBatchingUpdates
则是true
。