Appearance
setState
正确使用 setState
setState(partialState,callback)
- partialState: object | function
用于生产当前 state 合并的子集
- callback:function
state 更新之后被调用。
修改 state
在 react 中,我们不能直接通过修改 state 的值来让界面发生更新:
- 在 React 根据最新的 State 来重新渲染界面,但是这种方式的修改 React 并不知道数据发生了变化;
- React 并没有实现类似于 Vue2 中的 Object.defineProperty 或者 Vue3 中的 Proxy 的方式来监听数据的变化;
- 我们必须通过 setState 来告知 React 数据已经发生了变化;
不要直接修改 State
jsx
// 错误
this.state.comment = 'Hello';
// 正确
this.setSate({comment:'Hello'});
1
2
3
4
5
2
3
4
5
setState 源码
setState 方法是从 Component 中继承过来的。
javascript
Component.prototype.setstate= function(partialstate,callback)
invariant(
typeof partialState === 'object' ||
typeof partialState === ' function' ||
partialState == null,
'setstate(...):takes an object of state variables to update or a ! +
'function which returns an object of state variables.!',
);
this.updater.enqueueSetState(this, partialstate,callback,'setState'):
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Setstate(…): 接受一个状态变量对象来更新或一个!+ 函数返回一个状态变量对象。
State 的更新可能是异步的
处于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。 观察以下例子中 log 的值和 button 显示的 counter
javascript
import React, { Component } from "react";
export class SetStatePage extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
}
changeValue = (v) => {
this.setDate({
counter: this.state.counter + v
})
};
setCounter = () => {
this.changeValue(1);
};
render() {
return (
<div>
<h3>SetStatePage</h3>
<button onClick={this.setCounter}>{this.state.counter}</button>
</div>
);
}
}
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
为什么 setState 设计为异步呢?
- setState 设计为异步其实之前在 GitHub 上也有很多的讨论;
- React 核心成员(Redux 的作者)Dan Abramov 也有对应的回复,可以参考一下;
- https://github.com/facebook/react/issues/11527#issuecomment-360199710;
setState 设计为异步,可以显著的提升性能;
- 如果每次调用 setState 都进行一次更新,那么意味着 render 函数会被频繁调用,界面重新渲染,这样效率是很低的;
- 最好的办法应该是获取到多个更新,之后进行批量更新;
如何获取异步的结果
在回调中获取状态值
jsx
changeValue = (v) => {
// setState在合成事件中是异步的,这里异步其实就是批量的更新,达到优化的目的
this.setState(
{
counter: this.state.counter + v,
},
() => {
console.log("counter", this.state.counter);
}
);
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
生命周期
jsx
componentDidupdate (prevProps, provState, snapshot){
console.log(this.state.message)
}
1
2
3
2
3
使用定时器
在 setTimeout 或者原生 dom 事件中,setState 是同步
javascript
setTimeout(()=>{
this.setCounter();
},0)
1
2
3
2
3
原生事件中修改状态
在组件生命周期或 React 合成事件中,setState 是异步
javascript
componetDidMount(){
document.body.addEventListener('click',this.changeValue, false)
}
1
2
3
2
3
总结: setState 只有在合成事件和⽣生命周期函数中是异步的,在原⽣生事件和 setTimeout 中都是同步的,这⾥里里的异步其实是批量量更更新。
多个 state 的合并
jsx
// 如果有多个 state
function incrementMultiple() {
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
}
1
2
3
4
5
6
2
3
4
5
6
this.state.count 的值为多少呢?答案为 1
那哪些情况是不会合并呢?
jsx
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
传入函数,不会被合并。执行结果是 +3