[React18]1.7-类组件响应式视图、自动批处理
1、类组件响应式视图的实现与原理
通过state设置响应式视图,他是组件内私有的,受控于当前组件。
通过state的变化,就可以影响到视图的变化。
class Welcome extends React.Component {
state = {
msg: 'hello',
count: 0
}
render(){
return (
<div>
{this.state.msg}, {this.state.count}
</div>
);
}
}
let element = (
<Welcome />
);
这样就可以在页面中渲染msg
和count
这两个字段了。
那么怎么才能让state修改后视图跟着发生变化呢,首先不能像Vue那样直接对数据进行修改,在React中是不行的。
React类组件中式通过一个具体的方法**setState()
进行state数据的更新,从而触发render()
方法的重渲染操作**。
class Welcome extends React.Component {
state = {
msg: 'hello',
count: 0
}
handleClick = () => {
//this.state.msg = 'hi' //错误做法,永远不要这样去操作
//正确做法:
this.setState({
msg: 'hi'
});
}
render(){
console.log('render');
return (
<div>
<button onClick={this.handleClick}>点击</button>
{this.state.msg}, {this.state.count}
</div>
);
}
}
let element = (
<Welcome />
);
state改变视图的原理就是内部会重新调用render()
方法,俗称re-render
操作。
这里还有注意一点,setState()并不会影响其他state值,内部会完成合并的处理。
2、自动批处理
自动批处理,有助于减少在状态更改时发生的重新渲染次数。
在React18之前也有批处理的,但是在Promise、setTimeout、原生事件中是不起作用的。
实际上自动批处理指的是,同一时机多次调用setState()
方法的一种处理机制。
// 当调用两次setState()方法时
handleClick = () => {
this.setState({
msg: 'hi'
});
this.setState({
count: 1
});
}
这里的代码当点击触发后,虽然调用了两次setState()
方法,但是只会触发一次render()
方法的重新执行。
那么这就是所谓的自动批处理机制,这样是有助于性能的,减少重新执行的次数。
而且不管在什么时机下,都不会有问题的。
这个在React18版本之前并不是所有的情况都好用的,比如:定时器。
handleClick = () => {
setTimeout(()=>{
this.setState({
msg: 'hi'
});
this.setState({
count: 1
});
}, 2000)
}
上面代码在React18之前的版本中,将会触发两次render()
方法。
3、通过ReactDOM.flushSync
可以取消批处理操作
默认是自动批处理的,当然也可以改成不是自动批处理的方式,通过ReactDOM.flushSync
这个方法。
handleClick = () => {
ReactDOM.flushSync(()=>{
this.setState({
msg: 'hi'
});
})
ReactDOM.flushSync(()=>{
this.setState({
count: 1
});
})
}
以上代码中的setState()还是会被调用两次。
4、setState()
方法是异步处理
既然React18对多次调用采用的是自动批处理机制,那么就说明这个setState()
方法是异步的。
所以要注意方法调用完后,我们的state数据并不会立即发生变化,因为state可能会被先执行了:
<script type="text/babel">
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app);
class Welcome extends React.Component {
state = {
msg: 'hello',
count: 0
}
handleClick = () => {
this.setState({
// 页面显示的是1
count: this.state.count + 1
});
// 打印出的结果是0
console.log( this.state.count );
}
render(){
console.log('render');
return (
<div>
<button onClick={this.handleClick}>点击</button>
{this.state.msg}, {this.state.count}
</div>
);
}
}
let element = (
<Welcome />
);
root.render(element);
</script>
上面的代码里,第一次点击按钮后,打印出的结果是0,页面上显示的是1。说明是异步的。
在异步执行结束后的回调函数里,打印的值是异步之后的值:
handleClick = () => {
/* this.setState({
count: this.state.count + 1
});
console.log( this.state.count ); */
this.setState({
count: this.state.count + 1
}, ()=>{ //异步执行结束后的回调函数
console.log( this.state.count );
});
}
5、批处理就是合并多个setState(),提供了回调写法
可利用setState()
方法的第二个参数来保证数据更新后再去执行。
这里还要注意同样的数据修改只会修改一次:
handleClick = () => {
this.setState({
count: this.state.count + 1
});
this.setState({
count: this.state.count + 1
});
this.setState({
count: this.state.count + 1
});
}
上面代码里,3个方法里的内容是一样的,会被覆盖掉,最后做批处理时会合并,只会调用一次。
可利用setState()
的回调函数写法来保证每一次都能触发:
handleClick = () => {
/* this.setState({
count: this.state.count + 1
});
this.setState({
count: this.state.count + 1
});
this.setState({
count: this.state.count + 1
}); */
this.setState((state)=> ({count: state.count + 1}));
this.setState((state)=> ({count: state.count + 1}));
this.setState((state)=> ({count: state.count + 1}));
}
这样页面按钮点击一次,count会从0直接变成了3。