# 一.基础
React中constructor是唯一可以初始化state的地方,也可以把它理解成一个钩子函数,该函数最先执行且只执行一次。
更新状态不要直接修改this.state。虽然状态可以改变,但不会触发组件的更新。
应当使用this.setState(),该方法接收两种参数:对象或函数。
对象:即想要修改的state函数:接收两个函数,第一个函数接受两个参数,第一个是当前state,第二个是当前props,该函数返回一个对象,和直接传递对象参数是一样的,就是要修改的state;第二个函数参数是state改变后触发的回调。
# 二.setState同步还是异步?
# 1.setState执行后会发生什么
大致过程包括更新state,创建新的VNode,再经过diff算法比对差异,决定渲染哪一部分以及怎么渲染,最终形成最新的UI。主要生命周期:
- shouleComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
- componentWillReceiveProps(子组件的数据依赖于父组件时)
假如setState是同步更新的,每更新一次,这个过程都要完整执行一次,无疑会造成性能问题。事实上这些生命周期为纯函数,对性能还好,但是diff比较、更新DOM总消耗时间和性能吧。
// 如果setState是一个同步执行的机制,那么这个组件会被重新渲染100次,这对性能是一个相当大的消耗。
for ( let i = 0; i < 100; i++ ) {
this.setState( { num: this.state.num + 1 } );
}
1
2
3
4
2
3
4
此外为了批次和效能,多个setState有可能在执行过程中还会被合并,所以setState延时异步更新是很合理的
# 2.setState何时同步何时异步?
由React控制的事件处理程序,以及生命周期函数调用setState不会同步更新state 。大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。
React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等。
# 3.例子
# 异步
constructor() {
this.state = {
count: 10
}
this.handleClickOne = this.handleClickOne.bind(this)
this.handleClickTwo = this.handleClickTwo.bind(this)
}
render() {
return (
<button onClick={this.hanldeClickOne}>clickOne</button>
<button onClick={this.hanldeClickTwo}>clickTwo</button>
<button id="btn">clickTwo</button>
)
}
handleClickOne() {
this.setState({ count: this.state.count + 1})
console.log(this.state.count)//10
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 同步(绕过React)
// 例子1,原生事件中修改状态
componentDidMount() {
document.getElementById('btn').addEventListener('clcik', () => {
this.setState({ count: this.state.count + 1})
console.log(this.state.count)//11
})
}
//例子2,setTimeout
handleClickTwo() {
setTimeout(() => {
this.setState({ count: this.state.count + 1})
console.log(this.state.count)//11
}, 10)
}
// 例子3,使用回调函数
state = {
number:1
};
componentDidMount(){
this.setState({number:3},()=>{
console.log(this.state.number)//3
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 三.React是怎样控制异步和同步的呢?
# react同步
// 最终的结果只加了1
handleClick() {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
}
// 上面的代码等同于这样,count相当于一个快照,所以不管重复多少次,结果都是加1。
handleClick() {
const count = this.state.count
this.setState({
count: count + 1
})
this.setState({
count: count + 1
})
this.setState({
count: count + 1
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# react异步
// 第一个函数接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数。
increment(state, props) {
return {
count: state.count + 1
}
}
handleClick() {
this.setState(this.increment)
this.setState(this.increment)
this.setState(this.increment)
}
// 在同一个事件处理程序中不要混用,第一次执行setState,count为11,第二次执行,this.state仍然是没有更新的状态,所以this.state.count又打回了原形为10,加1以后变成11,最后再执行setState,所以最终count的结果是12。(render依然只执行一次)
increment(state, props) {
return {
count: state.count + 1
}
}
handleClick() {
this.setState(this.increment)
this.setState({ count: this.state.count + 1 })
this.setState(this.increment)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 回调函数
- 语法:
setState(updater[, callback])updater是要改变的state对象,callback是state导致的页面重新渲染的回调,等价于componentDidUpdate一般而言,在设置页面某些state的时候,需要先设置好state,然后再对页面的一些参数进行修改的时候,可以使用setState的回调函数。 - 使用
// 不在回调中使用参数,我们在设置state后立即使用state:
this.state = {foo: 1};
this.setState({foo: 123});
console.log(this.state.foo);// 1
// 在回调中调用设置好的state
this.state = {foo: 2};
this.setState({foo: 123}, ()=> {
console.log(foo);// 123
});
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 参考
https://www.jianshu.com/p/799b8a14ef96