第一次真正遇到这个问题,还是在写react的时候,大致问题就是,我在子组件中改变了从父组件传进来的props,恰好这个props是一个位于父组件state上的object,然后再调用从父组件中传进来的props中的函数去试图更新父组件的props,然而父组件并没有重新render
我们都知道在react中,要刷新一个组件,也就是触发render必须要是state或者props的改变才可以,先抛开props不管,一般情况下,改变sate就会触发render重新渲染,那么问题就来了
import React, {PureComponent} from 'react';
export default class Test extends PureComponent {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
clickButton = () => {
console.log('touched',this.state.count);
this.state.count += 1;
// this.setState({
// count: this.state.count + 1
// });
};
render() {
const {count} = this.state;
return (<div>
<p>{`已经点击${count}次`}</p>
<button onClick={this.clickButton}>
点击我测试
</button>
</div>);
}
}
我写一个按钮,点击一次触发clickButton,如果直接改变state的值,那么会出现这种情况
我点击了多次,state已经改变了,但是render并没有触发
其实raect的state是一个队列机制,react并不是每次setState,都会去执行真正的更新,react会将每一次setState放进队列,即进行state合并,完成后,再最后一起执行。
所以这里正确的写法应该是这样的
clickButton = () => {
console.log('touched',this.state.count);
let t = this.state.count+1;
// this.state.count += 1;
this.setState({
count: t
});
};
执行结果如下
但是,到这里还没有完,并不是说,我们换一种写法,能用就行就完事儿了,当我把代码再改变一下
clickButton = () => {
console.log('touched',this.state.count);
let t = this.state.count+1;
this.state.count += 1;
this.setState({
count: t
});
};
在setState之前,我先把count的值直接改变了,执行的结果如下
即使setState,还是没有重新render,那么这是什么原因呢
react不止对virtual dom有diff,对state依然有diff,diff就是每次更新状态前,去比对数据,如果不一样react才会重新渲染
所以当我在setState前直接改变state的值是同步的,改变之后,setState去执行异步更新时,发现这两个值是一样的,所以不会触发render重新渲染
为了印证是不是diff的结果造成的不会render,我们将代码再改变一下
clickButton = () => {
console.log('touched',this.state.count);
let t = this.state.count+1;
this.state.count += 1;
this.setState({
count: t+1
});
};
执行结果如下
很显然,setState的count值和直接设置的count值不一样,diif后,react就重新render渲染了页面
- 补充说明
react setSate是一个异步操作,所以在setSate之后,想要直接this.state.XXX获取最新的state是不可行的,因注意避免
- 2019.3.20补充:this.setState()虽然是一个异步操作,但其实能通过一些手段拿到最新的state
1、通过回调函数
this.setState({},()=>{console.log(this.state)})
2、在生命周期componentDidUpdate同样可以拿到最新state
3、用setTimeout()包裹一下
触发顺序如图